From 215a5b60392e799e5148f0baa4dd4e6cc7a5fd54 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Thu, 20 Jul 2017 03:51:44 +0200 Subject: [PATCH 1/6] gauge animation precision fix, visualization fix for turntable animations, mouse control repeat rate tweak, rudimentary framebuffer implementation --- Button.cpp | 7 --- Button.h | 2 +- Gauge.cpp | 2 +- Globals.cpp | 6 +++ Globals.h | 1 + Track.cpp | 21 ++++---- mouseinput.cpp | 2 + renderer.cpp | 144 +++++++++++++++++++++++++++++++++++++++++-------- renderer.h | 4 ++ version.h | 2 +- 10 files changed, 148 insertions(+), 43 deletions(-) diff --git a/Button.cpp b/Button.cpp index 4af9a652..06211b1d 100644 --- a/Button.cpp +++ b/Button.cpp @@ -14,13 +14,6 @@ http://mozilla.org/MPL/2.0/. #include "Console.h" #include "logs.h" -TButton::TButton() -{ - iFeedbackBit = 0; - bData = NULL; - Clear(); -}; - void TButton::Clear(int i) { pModelOn = nullptr; diff --git a/Button.h b/Button.h index 76737cd8..67061a47 100644 --- a/Button.h +++ b/Button.h @@ -36,7 +36,7 @@ class TButton play( PSound Sound ); public: - TButton(); + TButton() = default; void Clear(int const i = -1); inline void FeedbackBitSet(int const i) { iFeedbackBit = 1 << i; }; diff --git a/Gauge.cpp b/Gauge.cpp index 125c1a1d..c2206bb7 100644 --- a/Gauge.cpp +++ b/Gauge.cpp @@ -180,7 +180,7 @@ void TGauge::DecValue(double fNewDesired) void TGauge::UpdateValue( double fNewDesired, PSound Fallbacksound ) { - if( static_cast( std::round( ( fDesiredValue - fOffset ) / fScale ) ) == static_cast( fNewDesired ) ) { + if( static_cast( std::round( 100.0 * ( fDesiredValue - fOffset ) / fScale ) ) == static_cast( 100.0 * fNewDesired ) ) { return; } fDesiredValue = fNewDesired * fScale + fOffset; diff --git a/Globals.cpp b/Globals.cpp index 2a9aa97b..a21a4cac 100644 --- a/Globals.cpp +++ b/Globals.cpp @@ -87,6 +87,7 @@ float Global::Overcast { 0.1f }; // NOTE: all this weather stuff should be moved opengl_light Global::DayLight; int Global::DynamicLightCount { 3 }; bool Global::ScaleSpecularValues { true }; +bool Global::RenderShadows { false }; 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 @@ -544,6 +545,11 @@ void Global::ConfigParse(cParser &Parser) Parser.getTokens(); Parser >> Global::ScaleSpecularValues; } + else if( token == "shadows" ) { + // shadow render toggle + Parser.getTokens(); + Parser >> Global::RenderShadows; + } else if (token == "smoothtraction") { // podwójna jasność ambient diff --git a/Globals.h b/Globals.h index 4aa05ee3..948eae86 100644 --- a/Globals.h +++ b/Globals.h @@ -226,6 +226,7 @@ class Global static opengl_light DayLight; static int DynamicLightCount; static bool ScaleSpecularValues; + static bool RenderShadows; static int iSlowMotion; static TDynamicObject *changeDynObj; diff --git a/Track.cpp b/Track.cpp index 29e85e3f..433a344e 100644 --- a/Track.cpp +++ b/Track.cpp @@ -1267,31 +1267,32 @@ void TTrack::create_geometry( geometrybank_handle const &Bank ) { } vertex_array vertices; Segment->RenderLoft(vertices, origin, bpts1, iTrapezoid ? -4 : 4, fTexLength); + if( ( Bank != 0 ) && ( true == Geometry2.empty() ) ) { + Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); + } if( ( Bank == 0 ) && ( false == Geometry2.empty() ) ) { // special variant, replace existing data for a turntable track GfxRenderer.Replace( vertices, Geometry2[ 0 ] ); } - else { - Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); - } } if (TextureID1) { // szyny - generujemy dwie, najwyżej rysować siÄ™ bÄ™dzie jednÄ… vertex_array vertices; + if( ( Bank != 0 ) && ( true == Geometry1.empty() ) ) { + Segment->RenderLoft( vertices, origin, rpts1, iTrapezoid ? -nnumPts : nnumPts, fTexLength ); + Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); + vertices.clear(); // reuse the scratchpad + Segment->RenderLoft( vertices, origin, rpts2, iTrapezoid ? -nnumPts : nnumPts, fTexLength ); + Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); + } if( ( Bank == 0 ) && ( false == Geometry1.empty() ) ) { + // special variant, replace existing data for a turntable track Segment->RenderLoft( vertices, origin, rpts1, iTrapezoid ? -nnumPts : nnumPts, fTexLength ); GfxRenderer.Replace( vertices, Geometry1[ 0 ] ); vertices.clear(); // reuse the scratchpad Segment->RenderLoft( vertices, origin, rpts2, iTrapezoid ? -nnumPts : nnumPts, fTexLength ); GfxRenderer.Replace( vertices, Geometry1[ 1 ] ); } - else { - Segment->RenderLoft( vertices, origin, rpts1, iTrapezoid ? -nnumPts : nnumPts, fTexLength ); - Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); - vertices.clear(); // reuse the scratchpad - Segment->RenderLoft( vertices, origin, rpts2, iTrapezoid ? -nnumPts : nnumPts, fTexLength ); - Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) ); - } } break; case tt_Switch: // dla zwrotnicy dwa razy szyny diff --git a/mouseinput.cpp b/mouseinput.cpp index c38b64f1..97e1f6dc 100644 --- a/mouseinput.cpp +++ b/mouseinput.cpp @@ -125,9 +125,11 @@ mouse_input::button( int const Button, int const Action ) { // TODO: pass correct entity id once the missing systems are in place m_relay.post( mousecommand, 0, 0, Action, 0 ); m_updateaccumulator = 0.0; // prevent potential command repeat right after issuing one +/* if( mousecommand == user_command::mastercontrollerincrease ) { m_updateaccumulator -= 0.15; // extra pause on first increase of master controller } +*/ switch( mousecommand ) { case user_command::mastercontrollerincrease: case user_command::mastercontrollerdecrease: diff --git a/renderer.cpp b/renderer.cpp index 97690ec0..6800e1e4 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -120,10 +120,9 @@ opengl_renderer::Init( GLFWwindow *Window ) { m_lights.emplace_back( light ); } #ifdef EU07_USE_PICKING_FRAMEBUFFER + // pick buffer resources if( true == m_framebuffersupport ) { - // try to create the pick buffer. RGBA8 2D texture, 24 bit depth texture, 1024x512 - int const width { 1024 }; - int const height { 512 }; + // try to create the pick buffer. RGBA8 2D texture, 24 bit depth texture, 1024x1024 (height of 512 would suffice but, eh) // texture ::glGenTextures( 1, &m_picktexture ); ::glBindTexture( GL_TEXTURE_2D, m_picktexture ); @@ -131,18 +130,17 @@ 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, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL ); + ::glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, m_pickbuffersize, m_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, width, height ); - // create and set up the framebuffer + ::glRenderbufferStorageEXT( GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, m_pickbuffersize, m_pickbuffersize ); + // create and assemble the framebuffer ::glGenFramebuffersEXT( 1, &m_pickframebuffer ); ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, m_pickframebuffer ); ::glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_picktexture, 0 ); ::glFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, m_pickdepthbuffer ); - //------------------------- - //Does the GPU support current FBO configuration? + // check if we got it working GLenum status = ::glCheckFramebufferStatusEXT( GL_FRAMEBUFFER_EXT ); if( status == GL_FRAMEBUFFER_COMPLETE_EXT ) { WriteLog( "Picking framebuffer setup complete" ); @@ -154,6 +152,41 @@ opengl_renderer::Init( GLFWwindow *Window ) { ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 ); // switch back to primary render target for now } #endif + // shadowmap resources + if( ( true == Global::RenderShadows ) + && ( true == m_framebuffersupport ) ) { + // texture: + ::glGenTextures( 1, &m_shadowtexture ); + ::glBindTexture( GL_TEXTURE_2D, m_shadowtexture ); + ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + // enable shadow comparison: true (ie not in shadow) if r<=texture... + ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE ); + ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL ); + // ...shadow comparison should generate an INTENSITY result + ::glTexParameteri( GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY ); + ::glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, m_shadowbuffersize, m_shadowbuffersize, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL ); + // create and assemble the framebuffer + ::glGenFramebuffersEXT( 1, &m_shadowframebuffer ); + ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, m_shadowframebuffer ); + ::glDrawBuffer( GL_NONE ); // we won't be rendering colour data, so can skip the colour attachment + ::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 ); + if( status == GL_FRAMEBUFFER_COMPLETE_EXT ) { + WriteLog( "Shadows framebuffer setup complete" ); + } + else { + ErrorLog( "Shadows framebuffer setup failed" ); + m_framebuffersupport = false; + Global::RenderShadows = false; + } + ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 ); // switch back to primary render target for now + ::glDrawBuffer( GL_BACK ); + } + ::glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); // preload some common textures WriteLog( "Loading common gfx data..." ); @@ -205,7 +238,9 @@ opengl_renderer::Render_pass( rendermode const Mode ) { // TODO: run shadowmap pass before color -// ::glViewport( 0, 0, Global::ScreenWidth, Global::ScreenHeight ); +#ifdef EU07_USE_PICKING_FRAMEBUFFER + ::glViewport( 0, 0, Global::ScreenWidth, Global::ScreenHeight ); +#endif if( World.InitPerformed() ) { auto const skydomecolour = World.Environment.m_skydome.GetAverageColor(); ::glClearColor( skydomecolour.x, skydomecolour.y, skydomecolour.z, 0.0f ); // kolor nieba @@ -234,9 +269,33 @@ opengl_renderer::Render_pass( rendermode const Mode ) { break; } + case rendermode::shadows: { + +#ifdef EU07_USE_PICKING_FRAMEBUFFER + ::glViewport( 0, 0, m_pickbuffersize, m_pickbuffersize ); // TODO: separate shadow buffer +#endif + ::glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); + ::glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + + if( World.InitPerformed() ) { + // setup + m_renderpass.draw_range = 500.0f; // 1.5km square centered around camera + Render_projection(); + Render_camera(); + ::glDepthFunc( GL_LEQUAL ); + // render + // opaque parts... + Render_setup(); + Render( &World.Ground ); + } + break; + } + case rendermode::pickcontrols: { -// ::glViewport( 0, 0, 1024, 512 ); +#ifdef EU07_USE_PICKING_FRAMEBUFFER + ::glViewport( 0, 0, m_pickbuffersize, m_pickbuffersize ); +#endif ::glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); ::glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); @@ -259,7 +318,9 @@ opengl_renderer::Render_pass( rendermode const Mode ) { case rendermode::pickscenery: { -// ::glViewport( 0, 0, 1024, 512 ); +#ifdef EU07_USE_PICKING_FRAMEBUFFER + ::glViewport( 0, 0, m_pickbuffersize, m_pickbuffersize ); +#endif ::glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); ::glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); @@ -319,6 +380,12 @@ opengl_renderer::Render_projection() { */ case rendermode::shadows: { // TODO: set parallel projection + ::gluPerspective( + Global::FieldOfView, + 1.f, +// std::max( 1.0f, (float)Global::ScreenWidth ) / std::max( 1.0f, (float)Global::ScreenHeight ), + 2.f, + m_renderpass.draw_range * Global::fDistanceFactor ); break; } @@ -346,7 +413,7 @@ opengl_renderer::Render_camera() { break; } case rendermode::shadows: { - m_renderpass.camera.position() = Global::pCameraPosition - glm::dvec3{ Global::DayLight.direction * 750.f }; + m_renderpass.camera.position() = Global::pCameraPosition - glm::dvec3{ Global::DayLight.direction * 250.f }; m_renderpass.camera.position().y = std::max( 50.0, m_renderpass.camera.position().y ); // prevent shadow source from dipping too low viewmatrix = glm::lookAt( m_renderpass.camera.position(), @@ -1258,6 +1325,9 @@ opengl_renderer::Render( TSubModel *Submodel ) { // rysuj gdy element nieprzezroczysty switch( m_renderpass.draw_mode ) { case rendermode::color: { +// NOTE: code disabled as normalization marking doesn't take into account scaling propagation down hierarchy chains +// for the time being we'll do with enforced worst-case scaling method, when speculars are enabled +#ifdef EU07_USE_OPTIMIZED_NORMALIZATION switch( Submodel->m_normalizenormals ) { case TSubModel::normalize: { ::glEnable( GL_NORMALIZE ); break; } @@ -1266,6 +1336,11 @@ opengl_renderer::Render( TSubModel *Submodel ) { default: { break; } } +#else + if( true == m_renderspecular ) { + ::glEnable( GL_NORMALIZE ); + } +#endif // material configuration: // textures... if( Submodel->TextureID < 0 ) { // zmienialne skóry @@ -1280,6 +1355,7 @@ opengl_renderer::Render( TSubModel *Submodel ) { if( ( true == m_renderspecular ) && ( Global::DayLight.specular.a > 0.01f ) ) { // specular strength in legacy models is set uniformly to 150, 150, 150 so we scale it down for opaque elements ::glMaterialfv( GL_FRONT, GL_SPECULAR, glm::value_ptr( Submodel->f4Specular * Global::DayLight.specular.a * m_specularopaquescalefactor ) ); + ::glEnable( GL_RESCALE_NORMAL ); } // ...luminance if( Global::fLuminance < Submodel->fLight ) { @@ -1298,6 +1374,7 @@ opengl_renderer::Render( TSubModel *Submodel ) { // restore default (lack of) brightness ::glMaterialfv( GL_FRONT, GL_EMISSION, glm::value_ptr( colors::none ) ); } +#ifdef EU07_USE_OPTIMIZED_NORMALIZATION switch( Submodel->m_normalizenormals ) { case TSubModel::normalize: { ::glDisable( GL_NORMALIZE ); break; } @@ -1306,6 +1383,11 @@ opengl_renderer::Render( TSubModel *Submodel ) { default: { break; } } +#else + if( true == m_renderspecular ) { + ::glDisable( GL_NORMALIZE ); + } +#endif break; } case rendermode::shadows: @@ -1800,6 +1882,9 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { // renderowanie obiektów OpenGL if( Submodel->iAlpha & Submodel->iFlags & 0x2F ) // rysuj gdy element przezroczysty { +// NOTE: code disabled as normalization marking doesn't take into account scaling propagation down hierarchy chains +// for the time being we'll do with enforced worst-case scaling method, when speculars are enabled +#ifdef EU07_USE_OPTIMIZED_NORMALIZATION switch( Submodel->m_normalizenormals ) { case TSubModel::normalize: { ::glEnable( GL_NORMALIZE ); break; } @@ -1808,6 +1893,11 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { default: { break; } } +#else + if( true == m_renderspecular ) { + ::glEnable( GL_NORMALIZE ); + } +#endif // textures... if( Submodel->TextureID < 0 ) { // zmienialne skóry Bind( Submodel->ReplacableSkinId[ -Submodel->TextureID ] ); @@ -1838,6 +1928,7 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { // restore default (lack of) brightness ::glMaterialfv( GL_FRONT, GL_EMISSION, glm::value_ptr( colors::none ) ); } +#ifdef EU07_USE_OPTIMIZED_NORMALIZATION switch( Submodel->m_normalizenormals ) { case TSubModel::normalize: { ::glDisable( GL_NORMALIZE ); break; } @@ -1846,6 +1937,11 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { default: { break; } } +#else + if( true == m_renderspecular ) { + ::glDisable( GL_NORMALIZE ); + } +#endif } } else if( Submodel->eType == TP_FREESPOTLIGHT ) { @@ -1954,12 +2050,14 @@ opengl_renderer::Update_Pick_Control() { glm::dvec2 mousepos; glfwGetCursorPos( m_window, &mousepos.x, &mousepos.y ); mousepos.y = Global::ScreenHeight - mousepos.y; // cursor coordinates are flipped compared to opengl -/* + +#ifdef EU07_USE_PICKING_FRAMEBUFFER glm::ivec2 pickbufferpos { - mousepos.x * 1024.0f / Global::ScreenWidth, - mousepos.y * 512.0f / Global::ScreenHeight }; -*/ + mousepos.x * m_pickbuffersize / Global::ScreenWidth, + mousepos.y * m_pickbuffersize / Global::ScreenHeight }; +#else glm::ivec2 pickbufferpos{ mousepos }; +#endif unsigned char pickreadout[4]; ::glReadPixels( pickbufferpos.x, pickbufferpos.y, 1, 1, GL_BGRA, GL_UNSIGNED_BYTE, pickreadout ); @@ -1998,12 +2096,14 @@ opengl_renderer::Update_Pick_Node() { glm::dvec2 mousepos; glfwGetCursorPos( m_window, &mousepos.x, &mousepos.y ); mousepos.y = Global::ScreenHeight - mousepos.y; // cursor coordinates are flipped compared to opengl -/* + +#ifdef EU07_USE_PICKING_FRAMEBUFFER glm::ivec2 pickbufferpos { - mousepos.x * 1024.0f / Global::ScreenWidth, - mousepos.y * 512.0f / Global::ScreenHeight }; -*/ + mousepos.x * m_pickbuffersize / Global::ScreenWidth, + mousepos.y * m_pickbuffersize / Global::ScreenHeight }; +#else glm::ivec2 pickbufferpos{ mousepos }; +#endif unsigned char pickreadout[4]; ::glReadPixels( pickbufferpos.x, pickbufferpos.y, 1, 1, GL_BGRA, GL_UNSIGNED_BYTE, pickreadout ); @@ -2198,15 +2298,13 @@ opengl_renderer::Init_caps() { WriteLog( "Supported extensions:" + std::string((char *)glGetString( GL_EXTENSIONS )) ); WriteLog( std::string("Render path: ") + ( Global::bUseVBO ? "VBO" : "Display lists" ) ); -#ifdef EU07_USE_PICKING_FRAMEBUFFER if( GLEW_EXT_framebuffer_object ) { m_framebuffersupport = true; WriteLog( "Framebuffer objects enabled" ); } else { - WriteLog( "Framebuffer objects not supported, resorting to back buffer rendering" ); + WriteLog( "Framebuffer objects not supported, resorting to back buffer rendering where possible" ); } -#endif if( Global::iMultisampling ) WriteLog( "Using multisampling x" + std::to_string( 1 << Global::iMultisampling ) ); // ograniczenie maksymalnego rozmiaru tekstur - parametr dla skalowania tekstur diff --git a/renderer.h b/renderer.h index 4459e48d..1157e563 100644 --- a/renderer.h +++ b/renderer.h @@ -254,7 +254,11 @@ private: GLuint m_pickframebuffer { 0 }; // TODO: refactor pick framebuffer stuff into an object GLuint m_picktexture { 0 }; GLuint m_pickdepthbuffer { 0 }; + int m_pickbuffersize { 1024 }; // size of (square) textures bound with the pick framebuffer #endif + GLuint m_shadowframebuffer { 0 }; + GLuint m_shadowtexture { 0 }; + int m_shadowbuffersize { 2048 }; GLUquadricObj *m_quadric { nullptr }; // helper object for drawing debug mode scene elements geometry_handle m_billboardgeometry { 0, 0 }; diff --git a/version.h b/version.h index a4437670..dedc7d9c 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 17 -#define VERSION_MINOR 715 +#define VERSION_MINOR 717 #define VERSION_REVISION 0 From 8d5fde73710ba352da1519595c15973b3add8fc1 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Fri, 21 Jul 2017 01:27:20 +0200 Subject: [PATCH 2/6] completed basic separation of visual renderer from simulation --- AnimModel.cpp | 5 +- AnimModel.h | 2 + DynObj.cpp | 20 +++--- Ground.cpp | 7 +-- Model3d.cpp | 4 +- Track.cpp | 32 +++------- Track.h | 2 - renderer.cpp | 171 ++++++++++++++++++++++++++++++++++++-------------- renderer.h | 32 +++++----- sky.cpp | 15 +++-- sky.h | 16 ++--- stars.cpp | 8 ++- stars.h | 9 ++- stdafx.h | 2 +- uilayer.cpp | 2 +- 15 files changed, 197 insertions(+), 130 deletions(-) diff --git a/AnimModel.cpp b/AnimModel.cpp index 73b5524e..58d8ecb0 100644 --- a/AnimModel.cpp +++ b/AnimModel.cpp @@ -445,7 +445,7 @@ bool TAnimModel::Init(std::string const &asName, std::string const &asReplacable asText = asReplacableTexture.substr(1, asReplacableTexture.length() - 1); // zapamiÄ™tanie tekstu else if (asReplacableTexture != "none") m_materialdata.replacable_skins[1] = - GfxRenderer.GetTextureId( asReplacableTexture, "" ); + GfxRenderer.Fetch_Texture( asReplacableTexture, "" ); if( ( m_materialdata.replacable_skins[ 1 ] != 0 ) && ( GfxRenderer.Texture( m_materialdata.replacable_skins[ 1 ] ).has_alpha ) ) { // tekstura z kanaÅ‚em alfa - nie renderować w cyklu nieprzezroczystych @@ -606,6 +606,7 @@ int TAnimModel::Flags() //----------------------------------------------------------------------------- // 2011-03-16 funkcje renderowania z możliwoÅ›ciÄ… pochylania obiektów //----------------------------------------------------------------------------- +#ifdef EU07_USE_OLD_RENDERCODE void TAnimModel::Render( vector3 const &Position ) { RaAnimate(); // jednorazowe przeliczenie animacji RaPrepare(); @@ -617,7 +618,7 @@ void TAnimModel::RenderAlpha( vector3 const &Position ) { if( pModel ) // renderowanie rekurencyjne submodeli GfxRenderer.Render_Alpha( pModel, Material(), Position, vAngle ); }; - +#endif //--------------------------------------------------------------------------- bool TAnimModel::TerrainLoaded() { // zliczanie kwadratów kilometrowych (główna linia po Next) do tworznia tablicy diff --git a/AnimModel.h b/AnimModel.h index 17957f16..69866f56 100644 --- a/AnimModel.h +++ b/AnimModel.h @@ -164,8 +164,10 @@ class TAnimModel { bool Load(cParser *parser, bool ter = false); TAnimContainer * AddContainer(char *pName); TAnimContainer * GetContainer(char *pName); +#ifdef EU07_USE_OLD_RENDERCODE void Render( vector3 const &Position ); void RenderAlpha( vector3 const &Position ); +#endif int Flags(); void RaAnglesSet(double a, double b, double c) { diff --git a/DynObj.cpp b/DynObj.cpp index 05b2ac47..2a5c9254 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -4064,7 +4064,7 @@ void TDynamicObject::LoadMMediaFile(std::string BaseDir, std::string TypeName, { std::string nowheretexture = TextureTest(Global::asCurrentTexturePath + "nowhere"); // na razie prymitywnie if( false == nowheretexture.empty() ) { - m_materialdata.replacable_skins[ 4 ] = GfxRenderer.GetTextureId( nowheretexture, "", 9 ); + m_materialdata.replacable_skins[ 4 ] = GfxRenderer.Fetch_Texture( nowheretexture, "", 9 ); } if (m_materialdata.multi_textures > 0) { @@ -4076,7 +4076,7 @@ void TDynamicObject::LoadMMediaFile(std::string BaseDir, std::string TypeName, int skinindex = 0; std::string texturename; nameparser >> texturename; while( ( texturename != "" ) && ( skinindex < 4 ) ) { - m_materialdata.replacable_skins[ skinindex + 1 ] = GfxRenderer.GetTextureId( Global::asCurrentTexturePath + texturename, "" ); + m_materialdata.replacable_skins[ skinindex + 1 ] = GfxRenderer.Fetch_Texture( Global::asCurrentTexturePath + texturename, "" ); ++skinindex; texturename = ""; nameparser >> texturename; } @@ -4086,7 +4086,7 @@ void TDynamicObject::LoadMMediaFile(std::string BaseDir, std::string TypeName, // otherwise try the basic approach int skinindex = 0; do { - texture_handle texture = GfxRenderer.GetTextureId( Global::asCurrentTexturePath + ReplacableSkin + "," + std::to_string( skinindex + 1 ), "", Global::iDynamicFiltering, true ); + texture_handle texture = GfxRenderer.Fetch_Texture( Global::asCurrentTexturePath + ReplacableSkin + "," + std::to_string( skinindex + 1 ), "", Global::iDynamicFiltering, true ); if( texture == NULL ) { break; } @@ -4096,12 +4096,12 @@ void TDynamicObject::LoadMMediaFile(std::string BaseDir, std::string TypeName, m_materialdata.multi_textures = skinindex; if( m_materialdata.multi_textures == 0 ) { // zestaw nie zadziaÅ‚aÅ‚, próbujemy normanie - m_materialdata.replacable_skins[ 1 ] = GfxRenderer.GetTextureId( Global::asCurrentTexturePath + ReplacableSkin, "", Global::iDynamicFiltering ); + m_materialdata.replacable_skins[ 1 ] = GfxRenderer.Fetch_Texture( Global::asCurrentTexturePath + ReplacableSkin, "", Global::iDynamicFiltering ); } } } else { - m_materialdata.replacable_skins[ 1 ] = GfxRenderer.GetTextureId( Global::asCurrentTexturePath + ReplacableSkin, "", Global::iDynamicFiltering ); + m_materialdata.replacable_skins[ 1 ] = GfxRenderer.Fetch_Texture( Global::asCurrentTexturePath + ReplacableSkin, "", Global::iDynamicFiltering ); } if( GfxRenderer.Texture( m_materialdata.replacable_skins[ 1 ] ).has_alpha ) { // tekstura -1 z kanaÅ‚em alfa - nie renderować w cyklu nieprzezroczystych @@ -5424,13 +5424,13 @@ void TDynamicObject::DestinationSet(std::string to, std::string numer) std::string x = TextureTest(asBaseDir + numer + "@" + MoverParameters->TypeName); if (!x.empty()) { - m_materialdata.replacable_skins[ 4 ] = GfxRenderer.GetTextureId( x, "", 9 ); // rozmywania 0,1,4,5 nie nadajÄ… siÄ™ + m_materialdata.replacable_skins[ 4 ] = GfxRenderer.Fetch_Texture( x, "", 9 ); // rozmywania 0,1,4,5 nie nadajÄ… siÄ™ return; } x = TextureTest(asBaseDir + numer ); if (!x.empty()) { - m_materialdata.replacable_skins[ 4 ] = GfxRenderer.GetTextureId( x, "", 9 ); // rozmywania 0,1,4,5 nie nadajÄ… siÄ™ + m_materialdata.replacable_skins[ 4 ] = GfxRenderer.Fetch_Texture( x, "", 9 ); // rozmywania 0,1,4,5 nie nadajÄ… siÄ™ return; } if (to.empty()) @@ -5438,17 +5438,17 @@ void TDynamicObject::DestinationSet(std::string to, std::string numer) x = TextureTest(asBaseDir + to + "@" + MoverParameters->TypeName); // w pierwszej kolejnoÅ›ci z nazwÄ… FIZ/MMD if (!x.empty()) { - m_materialdata.replacable_skins[ 4 ] = GfxRenderer.GetTextureId( x, "", 9 ); // rozmywania 0,1,4,5 nie nadajÄ… siÄ™ + m_materialdata.replacable_skins[ 4 ] = GfxRenderer.Fetch_Texture( x, "", 9 ); // rozmywania 0,1,4,5 nie nadajÄ… siÄ™ return; } x = TextureTest(asBaseDir + to); // na razie prymitywnie if (!x.empty()) - m_materialdata.replacable_skins[ 4 ] = GfxRenderer.GetTextureId( x, "", 9 ); // rozmywania 0,1,4,5 nie nadajÄ… siÄ™ + m_materialdata.replacable_skins[ 4 ] = GfxRenderer.Fetch_Texture( x, "", 9 ); // rozmywania 0,1,4,5 nie nadajÄ… siÄ™ else { x = TextureTest(asBaseDir + "nowhere"); // jak nie znalazÅ‚ dedykowanej, to niech daje nowhere if (!x.empty()) - m_materialdata.replacable_skins[ 4 ] = GfxRenderer.GetTextureId( x, "", 9 ); + m_materialdata.replacable_skins[ 4 ] = GfxRenderer.Fetch_Texture( x, "", 9 ); } // Ra 2015-01: żeby zalogować błąd, trzeba by mieć pewność, że model używa tekstury nr 4 }; diff --git a/Ground.cpp b/Ground.cpp index b4640ea4..9f66a6e3 100644 --- a/Ground.cpp +++ b/Ground.cpp @@ -53,9 +53,8 @@ std::string LogComment; bool degenerate( glm::dvec3 const &Vertex1, glm::dvec3 const &Vertex2, glm::dvec3 const &Vertex3 ) { - return ( ( Vertex1 == Vertex2 ) - || ( Vertex2 == Vertex3 ) - || ( Vertex3 == Vertex1 ) ); +// 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(); } //--------------------------------------------------------------------------- @@ -1330,7 +1329,7 @@ TGroundNode * TGround::AddGroundNode(cParser *parser) *parser >> token; } str = token; - tmp->TextureID = GfxRenderer.GetTextureId( str, szTexturePath ); + tmp->TextureID = GfxRenderer.Fetch_Texture( str ); bool const clamps = ( tmp->TextureID ? GfxRenderer.Texture( tmp->TextureID ).traits.find( 's' ) != std::string::npos : diff --git a/Model3d.cpp b/Model3d.cpp index b77b9329..06a5f43c 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -341,7 +341,7 @@ int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic if( texture.find_first_of( "/\\" ) == texture.npos ) { texture.insert( 0, Global::asCurrentTexturePath ); } - TextureID = GfxRenderer.GetTextureId( texture, szTexturePath ); + TextureID = GfxRenderer.Fetch_Texture( texture ); // renderowanie w cyklu przezroczystych tylko jeÅ›li: // 1. Opacity=0 (przejÅ›ciowo <1, czy tam <100) oraz // 2. tekstura ma przezroczystość @@ -1670,7 +1670,7 @@ void TSubModel::BinInit(TSubModel *s, float4x4 *m, std::vector *t, pTexture = t->at(iTexture); if (pTexture.find_last_of("/\\") == std::string::npos) pTexture.insert(0, Global::asCurrentTexturePath); - TextureID = GfxRenderer.GetTextureId(pTexture, szTexturePath); + 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 |= diff --git a/Track.cpp b/Track.cpp index 433a344e..8b55c4ca 100644 --- a/Track.cpp +++ b/Track.cpp @@ -483,20 +483,20 @@ void TTrack::Load(cParser *parser, vector3 pOrigin, std::string name) { parser->getTokens(); *parser >> str; // railtex - TextureID1 = (str == "none" ? 0 : GfxRenderer.GetTextureId( - str, szTexturePath, - (iCategoryFlag & 1) ? Global::iRailProFiltering : - Global::iBallastFiltering)); + TextureID1 = ( + str == "none" ? + NULL : + GfxRenderer.Fetch_Texture( str ) ); parser->getTokens(); *parser >> fTexLength; // tex tile length if (fTexLength < 0.01) fTexLength = 4; // Ra: zabezpiecznie przed zawieszeniem parser->getTokens(); *parser >> str; // sub || railtex - TextureID2 = (str == "none" ? 0 : GfxRenderer.GetTextureId( - str, szTexturePath, - (eType == tt_Normal) ? Global::iBallastFiltering : - Global::iRailProFiltering)); + TextureID2 = ( + str == "none" ? + NULL : + GfxRenderer.Fetch_Texture( str ) ); parser->getTokens(3); *parser >> fTexHeight1 >> fTexWidth >> fTexSlope; if (iCategoryFlag & 4) @@ -1866,22 +1866,6 @@ void TTrack::EnvironmentReset() } }; -void TTrack::RenderDyn() -{ // renderowanie nieprzezroczystych fragmentów pojazdów - for( auto dynamic : Dynamics ) { - // sam sprawdza, czy VBO; zmienia kontekst VBO! - GfxRenderer.Render( dynamic ); - } -}; - -void TTrack::RenderDynAlpha() -{ // renderowanie przezroczystych fragmentów pojazdów - for( auto dynamic : Dynamics ) { - // sam sprawdza, czy VBO; zmienia kontekst VBO! - GfxRenderer.Render_Alpha( dynamic ); - } -}; - void TTrack::RenderDynSounds() { // odtwarzanie dźwiÄ™ków pojazdów jest niezależne od ich wyÅ›wietlania for( auto dynamic : Dynamics ) { diff --git a/Track.h b/Track.h index f7bf31e6..884d6907 100644 --- a/Track.h +++ b/Track.h @@ -236,8 +236,6 @@ public: /* void RaRenderVBO(int iPtr); // renderowanie z VBO sektora */ - void RenderDyn(); // renderowanie nieprzezroczystych pojazdów (oba tryby) - void RenderDynAlpha(); // renderowanie przezroczystych pojazdów (oba tryby) void RenderDynSounds(); // odtwarzanie dźwiÄ™ków pojazdów jest niezależne od ich wyÅ›wietlania void RaOwnerSet(TSubRect *o) { diff --git a/renderer.cpp b/renderer.cpp index 6800e1e4..4ddb6df5 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -190,9 +190,9 @@ opengl_renderer::Init( GLFWwindow *Window ) { ::glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); // preload some common textures WriteLog( "Loading common gfx data..." ); - m_glaretexture = GetTextureId( "fx\\lightglare", szTexturePath ); - m_suntexture = GetTextureId( "fx\\sun", szTexturePath ); - m_moontexture = GetTextureId( "fx\\moon", szTexturePath ); + m_glaretexture = Fetch_Texture( "fx\\lightglare" ); + m_suntexture = Fetch_Texture( "fx\\sun" ); + m_moontexture = Fetch_Texture( "fx\\moon" ); WriteLog( "...gfx data pre-loading done" ); // prepare basic geometry chunks auto const geometrybank = m_geometry.create_bank(); @@ -236,15 +236,27 @@ opengl_renderer::Render_pass( rendermode const Mode ) { case rendermode::color: { - // TODO: run shadowmap pass before color + 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 ); + 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 ); + } -#ifdef EU07_USE_PICKING_FRAMEBUFFER ::glViewport( 0, 0, Global::ScreenWidth, Global::ScreenHeight ); -#endif + if( World.InitPerformed() ) { auto const skydomecolour = World.Environment.m_skydome.GetAverageColor(); ::glClearColor( skydomecolour.x, skydomecolour.y, skydomecolour.z, 0.0f ); // kolor nieba } + else { + ::glClearColor( 51.0f / 255.0f, 102.0f / 255.0f, 85.0f / 255.0f, 1.0f ); // initial background Color + } ::glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); if( World.InitPerformed() ) { @@ -271,15 +283,14 @@ opengl_renderer::Render_pass( rendermode const Mode ) { case rendermode::shadows: { -#ifdef EU07_USE_PICKING_FRAMEBUFFER - ::glViewport( 0, 0, m_pickbuffersize, m_pickbuffersize ); // TODO: separate shadow buffer -#endif + ::glViewport( 0, 0, m_shadowbuffersize, m_shadowbuffersize ); + ::glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); - ::glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + ::glClear( GL_DEPTH_BUFFER_BIT ); if( World.InitPerformed() ) { // setup - m_renderpass.draw_range = 500.0f; // 1.5km square centered around camera + m_renderpass.draw_range = 500.0f; // 1.0km square centered around camera Render_projection(); Render_camera(); ::glDepthFunc( GL_LEQUAL ); @@ -355,9 +366,11 @@ opengl_renderer::Render_projection() { switch( m_renderpass.draw_mode ) { - case rendermode::color: +#ifndef EU07_USE_PICKING_FRAMEBUFFER case rendermode::pickcontrols: - case rendermode::pickscenery: { + case rendermode::pickscenery: +#endif + case rendermode::color: { ::gluPerspective( Global::FieldOfView / Global::ZoomFactor, std::max( 1.0f, (float)Global::ScreenWidth ) / std::max( 1.0f, (float)Global::ScreenHeight ), @@ -365,19 +378,20 @@ opengl_renderer::Render_projection() { m_renderpass.draw_range * Global::fDistanceFactor ); break; } -/* - // version for the smaller pickbuffer. ultimately scratched because resolution isn't comfortable/reliable enough for the user +#ifdef EU07_USE_PICKING_FRAMEBUFFER case rendermode::pickcontrols: case rendermode::pickscenery: { - // TODO: render to buffer and scissor test for pick modes + // 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 / 1024.0f ), + 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 ); break; } -*/ +#endif case rendermode::shadows: { // TODO: set parallel projection ::gluPerspective( @@ -447,6 +461,9 @@ opengl_renderer::Render_setup( bool const Alpha ) { case rendermode::color: { ::glEnable( GL_LIGHTING ); ::glShadeModel( GL_SMOOTH ); + if( Global::iMultisampling ) { + ::glEnable( GL_MULTISAMPLE ); + } // setup fog if( Global::fFogEnd > 0 ) { // fog setup @@ -474,9 +491,12 @@ opengl_renderer::Render_setup( bool const Alpha ) { case rendermode::shadows: case rendermode::pickcontrols: case rendermode::pickscenery: { - ::glDisable( GL_FOG ); ::glDisable( GL_LIGHTING ); ::glShadeModel( GL_FLAT ); + if( Global::iMultisampling ) { + ::glDisable( GL_MULTISAMPLE ); + } + ::glDisable( GL_FOG ); if( m_texenvmode != m_renderpass.draw_mode ) { // solid colour with texture alpha @@ -508,19 +528,32 @@ opengl_renderer::Render( world_environment *Environment ) { } Bind( NULL ); - ::glDisable( GL_LIGHTING ); ::glDisable( GL_DEPTH_TEST ); ::glDepthMask( GL_FALSE ); ::glPushMatrix(); + // skydome Environment->m_skydome.Render(); if( true == Global::bUseVBO ) { // skydome uses a custom vbo which could potentially confuse the main geometry system. hardly elegant but, eh opengl_vbogeometrybank::reset(); } - Environment->m_stars.render(); + // stars + if( Environment->m_stars.m_stars != nullptr ) { + // setup + ::glPushMatrix(); + ::glRotatef( Environment->m_stars.m_latitude, 1.f, 0.f, 0.f ); // ustawienie osi OY na północ + ::glRotatef( -std::fmod( (float)Global::fTimeAngleDeg, 360.f ), 0.f, 1.f, 0.f ); // obrót dobowy osi OX + ::glPointSize( 2.f ); + // render + GfxRenderer.Render( Environment->m_stars.m_stars, nullptr, 1.0 ); + // post-render cleanup + ::glPointSize( 3.f ); + ::glPopMatrix(); + } + // celestial bodies float const duskfactor = 1.0f - clamp( std::abs( Environment->m_sun.getAngle() ), 0.0f, 12.0f ) / 12.0f; glm::vec3 suncolor = interpolate( glm::vec3( 255.0f / 255.0f, 242.0f / 255.0f, 231.0f / 255.0f ), @@ -601,10 +634,26 @@ opengl_renderer::Render( world_environment *Environment ) { ::glPopAttrib(); // clouds - Environment->m_clouds.Render( - interpolate( Environment->m_skydome.GetAverageColor(), suncolor, duskfactor * 0.25f ) - * ( 1.0f - Global::Overcast * 0.5f ) // overcast darkens the clouds - * 2.5f ); // arbitrary adjustment factor + if( Environment->m_clouds.mdCloud ) { + // setup + Disable_Lights(); + ::glEnable( GL_LIGHTING ); + ::glLightModelfv( + GL_LIGHT_MODEL_AMBIENT, + glm::value_ptr( + interpolate( Environment->m_skydome.GetAverageColor(), suncolor, duskfactor * 0.25f ) + * ( 1.0f - Global::Overcast * 0.5f ) // overcast darkens the clouds + * 2.5f // arbitrary adjustment factor + ) ); + // render + Render( Environment->m_clouds.mdCloud, nullptr, 100.0 ); + Render_Alpha( Environment->m_clouds.mdCloud, nullptr, 100.0 ); + // post-render cleanup + GLfloat noambient[] = { 0.0f, 0.0f, 0.0f, 1.0f }; + ::glLightModelfv( GL_LIGHT_MODEL_AMBIENT, noambient ); + ::glEnable( GL_LIGHT0 ); // other lights will be enabled during lights update + ::glDisable( GL_LIGHTING ); + } Global::DayLight.apply_angle(); Global::DayLight.apply_intensity(); @@ -655,7 +704,7 @@ opengl_renderer::Vertices( geometry_handle const &Geometry ) const { // texture methods texture_handle -opengl_renderer::GetTextureId( std::string Filename, std::string const &Dir, int const Filter, bool const Loadnow ) { +opengl_renderer::Fetch_Texture( std::string const &Filename, std::string const &Dir, int const Filter, bool const Loadnow ) { return m_textures.create( Filename, Dir, Filter, Loadnow ); } @@ -854,7 +903,7 @@ opengl_renderer::Render( TSubRect *Groundsubcell ) { #ifdef EU07_SCENERY_EDITOR // memcells if( EditorModeFlag ) { - for( auto const memcell : m_memcells ) { + for( auto const memcell : Groundsubcell->m_memcells ) { Render( memcell ); } } @@ -888,7 +937,7 @@ opengl_renderer::Render( TSubRect *Groundsubcell ) { #ifdef EU07_SCENERY_EDITOR // memcells if( EditorModeFlag ) { - for( auto const memcell : m_memcells ) { + for( auto const memcell : Groundsubcell->m_memcells ) { ::glColor3fv( glm::value_ptr( pick_color( m_picksceneryitems.size() + 1 ) ) ); Render( memcell ); } @@ -961,7 +1010,14 @@ 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 ) // renderowanie rekurencyjne submodeli + Render( Node->Model->pModel, Node->Model->Material(), Node->pCenter - m_renderpass.camera.position(), Node->Model->vAngle ); +#endif return true; } @@ -1559,19 +1615,35 @@ opengl_renderer::Render( TTrack *Track ) { void opengl_renderer::Render( TMemCell *Memcell ) { - ::glPushAttrib( GL_ENABLE_BIT ); -// ::glDisable( GL_LIGHTING ); - ::glDisable( GL_TEXTURE_2D ); -// ::glEnable( GL_BLEND ); ::glPushMatrix(); - - auto const position = Memcell->Position(); + auto const position = Memcell->Position() - m_renderpass.camera.position(); ::glTranslated( position.x, position.y + 0.5, position.z ); - ::glColor3f( 0.36f, 0.75f, 0.35f ); - ::gluSphere( m_quadric, 0.35, 4, 2 ); + + switch( m_renderpass.draw_mode ) { + case rendermode::color: { + ::glPushAttrib( GL_ENABLE_BIT ); + ::glDisable( GL_TEXTURE_2D ); + ::glColor3f( 0.36f, 0.75f, 0.35f ); + + ::gluSphere( m_quadric, 0.35, 4, 2 ); + + ::glPopAttrib(); + break; + } + case rendermode::pickscenery: + case rendermode::shadows: { + ::gluSphere( m_quadric, 0.35, 4, 2 ); + break; + } + case rendermode::pickcontrols: { + break; + } + default: { + break; + } + } ::glPopMatrix(); - ::glPopAttrib(); } bool @@ -1615,8 +1687,11 @@ opengl_renderer::Render_Alpha( TSubRect *Groundsubcell ) { Render_Alpha( node ); // przezroczyste z mieszanych modeli for( node = Groundsubcell->nRenderAlpha; node; node = node->nNext3 ) Render_Alpha( node ); // przezroczyste modele - for( int j = 0; j < Groundsubcell->iTracks; ++j ) - Groundsubcell->tTracks[ j ]->RenderDynAlpha(); // przezroczyste fragmenty pojazdów na torach + for( int trackidx = 0; trackidx < Groundsubcell->iTracks; ++trackidx ) { + for( auto dynamic : Groundsubcell->tTracks[ trackidx ]->Dynamics ) { + Render_Alpha( dynamic ); // przezroczyste fragmenty pojazdów na torach + } + } return true; } @@ -1677,7 +1752,13 @@ 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 + Render_Alpha( Node->Model->pModel, Node->Model->Material(), Node->pCenter - m_renderpass.camera.position(), Node->Model->vAngle ); +#endif return true; } @@ -2137,15 +2218,11 @@ opengl_renderer::Update( double const Deltatime ) { // adjust draw ranges etc, based on recent performance auto const framerate = 1000.0f / (m_drawtime / 20.0f); - // NOTE: until we have quadtree in place we have to rely on the legacy rendering - // once this is resolved we should be able to simply adjust draw range - int targetsegments; float targetfactor; - - if( framerate > 90.0 ) { targetsegments = 400; targetfactor = 3.0f; } - else if( framerate > 60.0 ) { targetsegments = 225; targetfactor = 1.5f; } - else if( framerate > 30.0 ) { targetsegments = 90; targetfactor = Global::ScreenHeight / 768.0f; } - else { targetsegments = 9; targetfactor = Global::ScreenHeight / 768.0f * 0.75f; } + 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; } if( targetfactor > Global::fDistanceFactor ) { diff --git a/renderer.h b/renderer.h index 1157e563..8095a188 100644 --- a/renderer.h +++ b/renderer.h @@ -119,21 +119,6 @@ public: // main draw call. returns false on error bool Render(); - // render sub-methods, temporarily exposed until we complete migrating render code to the renderer - bool - Render( TDynamicObject *Dynamic ); - bool - Render( TModel3d *Model, material_data const *Material, Math3D::vector3 const &Position, Math3D::vector3 const &Angle ); - bool - Render( TModel3d *Model, material_data const *Material, double const Squaredistance ); - void - Render( TSubModel *Submodel ); - bool - Render_Alpha( TDynamicObject *Dynamic ); - bool - Render_Alpha( TModel3d *Model, material_data const *Material, Math3D::vector3 const &Position, Math3D::vector3 const &Angle ); - bool - Render_Alpha( TModel3d *Model, material_data const *Material, double const Squaredistance ); // 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 @@ -153,7 +138,7 @@ public: Vertices( geometry_handle const &Geometry ) const; // texture methods texture_handle - GetTextureId( std::string Filename, std::string const &Dir, int const Filter = -1, bool const Loadnow = true ); + Fetch_Texture( std::string const &Filename, std::string const &Dir = szTexturePath, int const Filter = -1, bool const Loadnow = true ); void Bind( texture_handle const Texture ); opengl_texture const & @@ -225,6 +210,14 @@ private: Render( TSubRect *Groundsubcell ); bool Render( TGroundNode *Node ); + bool + Render( TDynamicObject *Dynamic ); + bool + Render( TModel3d *Model, material_data const *Material, Math3D::vector3 const &Position, Math3D::vector3 const &Angle ); + bool + Render( TModel3d *Model, material_data const *Material, double const Squaredistance ); + void + Render( TSubModel *Submodel ); void Render( TTrack *Track ); bool @@ -237,6 +230,12 @@ private: Render_Alpha( TSubRect *Groundsubcell ); bool Render_Alpha( TGroundNode *Node ); + bool + Render_Alpha( TDynamicObject *Dynamic ); + bool + Render_Alpha( TModel3d *Model, material_data const *Material, Math3D::vector3 const &Position, Math3D::vector3 const &Angle ); + bool + Render_Alpha( TModel3d *Model, material_data const *Material, double const Squaredistance ); void Render_Alpha( TSubModel *Submodel ); void @@ -277,6 +276,7 @@ private: bool m_framebuffersupport { false }; rendermode m_texenvmode { rendermode::color }; // last configured texture environment renderpass_config m_renderpass; +// std::stack m_renderpasses; bool m_renderspecular { false }; // controls whether to include specular component in the calculations std::vector m_picksceneryitems; std::vector m_pickcontrolsitems; diff --git a/sky.cpp b/sky.cpp index 284825ab..86aba2fe 100644 --- a/sky.cpp +++ b/sky.cpp @@ -9,24 +9,22 @@ http://mozilla.org/MPL/2.0/. #include "stdafx.h" #include "sky.h" -#include "Logs.h" #include "Globals.h" #include "MdlMngr.h" //--------------------------------------------------------------------------- -GLfloat lightPos[4] = {0.0f, 0.0f, 0.0f, 1.0f}; +//GLfloat lightPos[4] = {0.0f, 0.0f, 0.0f, 1.0f}; -TSky::~TSky(){}; +void TSky::Init() { -TSky::TSky(){}; + if( ( Global::asSky != "1" ) + && ( Global::asSky != "0" ) ) { -void TSky::Init() -{ - WriteLog( "Clouds init" ); - if ((Global::asSky != "1") && (Global::asSky != "0")) mdCloud = TModelsManager::GetModel( Global::asSky ); + } }; +#ifdef EU07_USE_OLD_RENDERCODE void TSky::Render( glm::vec3 const &Tint ) { if (mdCloud) @@ -45,5 +43,6 @@ void TSky::Render( glm::vec3 const &Tint ) ::glDisable( GL_LIGHTING ); } }; +#endif //--------------------------------------------------------------------------- diff --git a/sky.h b/sky.h index ebee49a2..4b544e0e 100644 --- a/sky.h +++ b/sky.h @@ -12,16 +12,18 @@ http://mozilla.org/MPL/2.0/. #include "Model3d.h" -class TSky -{ - private: - TModel3d *mdCloud; +class TSky { - public: - TSky(); - ~TSky(); + friend class opengl_renderer; + +private: + TModel3d *mdCloud { nullptr }; + +public: void Init(); +#ifdef EU07_USE_OLD_RENDERCODE void Render( glm::vec3 const &Tint = glm::vec3(1.0f, 1.0f, 1.0f) ); +#endif }; //--------------------------------------------------------------------------- diff --git a/stars.cpp b/stars.cpp index 08b2b926..ebbd84a6 100644 --- a/stars.cpp +++ b/stars.cpp @@ -2,6 +2,7 @@ #include "stdafx.h" #include "stars.h" #include "globals.h" +#include "MdlMngr.h" ////////////////////////////////////////////////////////////////////////////////////////// // cStars -- simple starfield model, simulating appearance of starry sky @@ -9,9 +10,9 @@ void cStars::init() { - m_stars.LoadFromFile( "models\\skydome_stars.t3d", false ); + m_stars = TModelsManager::GetModel( "models\\skydome_stars.t3d", false ); } - +#ifdef EU07_USE_OLD_RENDERCODE void cStars::render() { // setup @@ -27,4 +28,5 @@ cStars::render() { ::glPointSize( 3.0f ); ::glPopMatrix(); -} \ No newline at end of file +} +#endif diff --git a/stars.h b/stars.h index d5eb7989..dc579ff5 100644 --- a/stars.h +++ b/stars.h @@ -8,13 +8,16 @@ class cStars { + friend class opengl_renderer; + public: // types: // methods: void init(); - void render(); - +#ifdef EU07_USE_OLD_RENDERCODE + void render(); +#endif // constructors: // deconstructor: @@ -29,5 +32,5 @@ private: // members: float m_longitude{ 19.0f }; // geograpic coordinates hardcoded roughly to Poland location, for the time being float m_latitude{ 52.0f }; - TModel3d m_stars; + TModel3d *m_stars { nullptr }; }; diff --git a/stdafx.h b/stdafx.h index cca09510..ca6107c9 100644 --- a/stdafx.h +++ b/stdafx.h @@ -91,4 +91,4 @@ #include #include -#include "openglmatrixstack.h" \ No newline at end of file +#include "openglmatrixstack.h" diff --git a/uilayer.cpp b/uilayer.cpp index 0a98e41b..0b1474f9 100644 --- a/uilayer.cpp +++ b/uilayer.cpp @@ -96,7 +96,7 @@ void ui_layer::set_background( std::string const &Filename ) { if( false == Filename.empty() ) { - m_background = GfxRenderer.GetTextureId( Filename, szTexturePath ); + m_background = GfxRenderer.Fetch_Texture( Filename ); } else { m_background = NULL; From ba5a3613adf29fdd811656b09f0a400079fa1651 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Sat, 22 Jul 2017 00:14:07 +0200 Subject: [PATCH 3/6] build 170721. increased size of light points at long distances --- Ground.cpp | 8 -------- Model3d.cpp | 6 ++---- renderer.cpp | 35 +++++++++++++++++++++++++---------- usefull.h | 9 +++++++++ version.h | 2 +- 5 files changed, 37 insertions(+), 23 deletions(-) diff --git a/Ground.cpp b/Ground.cpp index 9f66a6e3..91f84e87 100644 --- a/Ground.cpp +++ b/Ground.cpp @@ -49,14 +49,6 @@ extern "C" bool bCondition; // McZapkie: do testowania warunku na event multiple std::string LogComment; -// tests whether provided points form a degenerate triangle -bool -degenerate( glm::dvec3 const &Vertex1, glm::dvec3 const &Vertex2, glm::dvec3 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(); -} - //--------------------------------------------------------------------------- // Obiekt renderujÄ…cy siatkÄ™ jest sztucznie tworzonym obiektem pomocniczym, // grupujÄ…cym siatki obiektów dla danej tekstury. Obiektami skÅ‚adowymi mogÄ… diff --git a/Model3d.cpp b/Model3d.cpp index 06a5f43c..e2168583 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -460,10 +460,8 @@ int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic >> Vertices[i].texture.t; if (i % 3 == 2) { // jeżeli wczytano 3 punkty - if (Vertices[i ].position == Vertices[i - 1].position - || Vertices[i - 1].position == Vertices[i - 2].position - || Vertices[i - 2].position == Vertices[i ].position) - { // jeżeli punkty siÄ™ nakÅ‚adajÄ… na siebie + if( true == degenerate( Vertices[ i ].position, Vertices[ i - 1 ].position, Vertices[ i - 2 ].position ) ) { + // jeżeli punkty siÄ™ nakÅ‚adajÄ… na siebie --facecount; // o jeden trójkÄ…t mniej iNumVerts -= 3; // czyli o 3 wierzchoÅ‚ki i -= 3; // wczytanie kolejnego w to miejsce diff --git a/renderer.cpp b/renderer.cpp index 4ddb6df5..07453165 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -1493,32 +1493,42 @@ opengl_renderer::Render( TSubModel *Submodel ) { // spotlights are only rendered in colour mode(s) case rendermode::color: { auto const &modelview = OpenGLMatrices.data( GL_MODELVIEW ); - auto const lightcenter = modelview * glm::vec4( 0.0f, 0.0f, -0.05f, 1.0f ); // pozycja punktu Å›wiecÄ…cego wzglÄ™dem kamery - Submodel->fCosViewAngle = glm::dot( glm::normalize( modelview * glm::vec4( 0.0f, 0.0f, -1.0f, 1.0f ) - lightcenter ), glm::normalize( -lightcenter ) ); + auto const lightcenter = + modelview + * interpolate( + glm::vec4( 0.f, 0.f, -0.05f, 1.f ), + glm::vec4( 0.f, 0.f, -0.25f, 1.f ), + static_cast( TSubModel::fSquareDist / Submodel->fSquareMaxDist ) ); // pozycja punktu Å›wiecÄ…cego wzglÄ™dem kamery + Submodel->fCosViewAngle = glm::dot( glm::normalize( modelview * glm::vec4( 0.f, 0.f, -1.f, 1.f ) - lightcenter ), glm::normalize( -lightcenter ) ); - if( Submodel->fCosViewAngle > Submodel->fCosFalloffAngle ) // kÄ…t wiÄ™kszy niż maksymalny stożek swiatÅ‚a - { - float lightlevel = 1.0f; // TODO, TBD: parameter to control light strength - // view angle attenuation + if( Submodel->fCosViewAngle > Submodel->fCosFalloffAngle ) { + // kÄ…t wiÄ™kszy niż maksymalny stożek swiatÅ‚a + float lightlevel = 1.f; // TODO, TBD: parameter to control light strength + // view angle attenuation float const anglefactor = ( Submodel->fCosViewAngle - Submodel->fCosFalloffAngle ) / ( 1.0f - Submodel->fCosFalloffAngle ); // distance attenuation. NOTE: since it's fixed pipeline with built-in gamma correction we're using linear attenuation // we're capping how much effect the distance attenuation can have, otherwise the lights get too tiny at regular distances float const distancefactor = static_cast( std::max( 0.5, ( Submodel->fSquareMaxDist - TSubModel::fSquareDist ) / ( Submodel->fSquareMaxDist * Global::fDistanceFactor ) ) ); - if( lightlevel > 0.0f ) { + if( lightlevel > 0.f ) { // material configuration: ::glPushAttrib( GL_ENABLE_BIT | GL_CURRENT_BIT | GL_COLOR_BUFFER_BIT | GL_POINT_BIT ); Bind( 0 ); - ::glPointSize( std::max( 2.0f, 4.0f * distancefactor * anglefactor ) ); + ::glPointSize( std::max( 3.f, 5.f * distancefactor * anglefactor ) ); ::glColor4f( Submodel->f4Diffuse[ 0 ], Submodel->f4Diffuse[ 1 ], Submodel->f4Diffuse[ 2 ], lightlevel * anglefactor ); ::glDisable( GL_LIGHTING ); ::glEnable( GL_BLEND ); + ::glPushMatrix(); + ::glLoadIdentity(); + ::glTranslatef( lightcenter.x, lightcenter.y, lightcenter.z ); // poczÄ…tek ukÅ‚adu zostaje bez zmian + // main draw call m_geometry.draw( Submodel->m_geometry ); // post-draw reset + ::glPopMatrix(); ::glPopAttrib(); } } @@ -2031,8 +2041,13 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { // NOTE: we're forced here to redo view angle calculations etc, because this data isn't instanced but stored along with the single mesh // TODO: separate instance data from reusable geometry auto const &modelview = OpenGLMatrices.data( GL_MODELVIEW ); - auto const lightcenter = modelview * glm::vec4( 0.0f, 0.0f, -0.05f, 1.0f ); // pozycja punktu Å›wiecÄ…cego wzglÄ™dem kamery - Submodel->fCosViewAngle = glm::dot( glm::normalize( modelview * glm::vec4( 0.0f, 0.0f, -1.0f, 1.0f ) - lightcenter ), glm::normalize( -lightcenter ) ); + auto const lightcenter = + modelview + * interpolate( + glm::vec4( 0.f, 0.f, -0.05f, 1.f ), + glm::vec4( 0.f, 0.f, -0.10f, 1.f ), + static_cast( TSubModel::fSquareDist / Submodel->fSquareMaxDist ) ); // pozycja punktu Å›wiecÄ…cego wzglÄ™dem kamery + Submodel->fCosViewAngle = glm::dot( glm::normalize( modelview * glm::vec4( 0.f, 0.f, -1.f, 1.f ) - lightcenter ), glm::normalize( -lightcenter ) ); float glarelevel = 0.6f; // luminosity at night is at level of ~0.1, so the overall resulting transparency is ~0.5 at full 'brightness' if( Submodel->fCosViewAngle > Submodel->fCosFalloffAngle ) { diff --git a/usefull.h b/usefull.h index cd747876..6474da98 100644 --- a/usefull.h +++ b/usefull.h @@ -71,4 +71,13 @@ interpolate( Type_ const &First, Type_ const &Second, double const Factor ) { return ( First * ( 1.0 - Factor ) ) + ( Second * Factor ); } +// tests whether provided points form a degenerate triangle +template +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(); +} + //--------------------------------------------------------------------------- diff --git a/version.h b/version.h index dedc7d9c..a54feaa0 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 17 -#define VERSION_MINOR 717 +#define VERSION_MINOR 721 #define VERSION_REVISION 0 From d2d93616fa1889680f4ceda328a4226ea4f9f668 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Mon, 24 Jul 2017 01:26:05 +0200 Subject: [PATCH 4/6] build 170424. basic shadow mapping implementation, multi texturing unit setup --- Globals.cpp | 9 + Globals.h | 6 + McZapkie/mctools.h | 5 + World.cpp | 11 +- openglmatrixstack.h | 17 +- renderer.cpp | 642 +++++++++++++++++++++++++++++++++++--------- renderer.h | 41 ++- uilayer.cpp | 10 +- uilayer.h | 3 + version.h | 2 +- 10 files changed, 601 insertions(+), 145 deletions(-) diff --git a/Globals.cpp b/Globals.cpp index a21a4cac..6073b44d 100644 --- a/Globals.cpp +++ b/Globals.cpp @@ -88,6 +88,7 @@ opengl_light Global::DayLight; int Global::DynamicLightCount { 3 }; bool Global::ScaleSpecularValues { true }; bool Global::RenderShadows { false }; +Global::shadowtune_t Global::shadowtune = { 2048, 200.0f, 150.0f, 100.0f }; bool Global::bRollFix = true; // czy wykonać przeliczanie przechyÅ‚ki bool Global::bJoinEvents = false; // czy grupować eventy o tych samych nazwach int Global::iHiddenEvents = 1; // czy łączyć eventy z torami poprzez nazwÄ™ toru @@ -550,6 +551,14 @@ void Global::ConfigParse(cParser &Parser) Parser.getTokens(); Parser >> Global::RenderShadows; } + else if( token == "shadowtune" ) { + Parser.getTokens( 4, false ); + Parser + >> Global::shadowtune.map_size + >> Global::shadowtune.width + >> Global::shadowtune.depth + >> Global::shadowtune.distance; + } else if (token == "smoothtraction") { // podwójna jasność ambient diff --git a/Globals.h b/Globals.h index 948eae86..752aca31 100644 --- a/Globals.h +++ b/Globals.h @@ -227,6 +227,12 @@ class Global static int DynamicLightCount; static bool ScaleSpecularValues; static bool RenderShadows; + static struct shadowtune_t { + unsigned int map_size; + float width; + float depth; + float distance; + } shadowtune; static int iSlowMotion; static TDynamicObject *changeDynObj; diff --git a/McZapkie/mctools.h b/McZapkie/mctools.h index 8f02ab11..55a65447 100644 --- a/McZapkie/mctools.h +++ b/McZapkie/mctools.h @@ -98,6 +98,11 @@ std::string to_string( glm::tvec3 const &Value ) { return to_string( Value.x, 2 ) + ", " + to_string( Value.y, 2 ) + ", " + to_string( Value.z, 2 ); } +template +std::string to_string( glm::tvec4 const &Value, int const Width = 2 ) { + return to_string( Value.x, Width ) + ", " + to_string( Value.y, Width ) + ", " + to_string( Value.z, Width ) + ", " + to_string( Value.w, Width ); +} + int stol_def(const std::string & str, const int & DefaultValue); std::string ToLower(std::string const &text); diff --git a/World.cpp b/World.cpp index 198f4ed6..f11c1e80 100644 --- a/World.cpp +++ b/World.cpp @@ -32,6 +32,8 @@ http://mozilla.org/MPL/2.0/. #include "uilayer.h" #include "translation.h" +//#define EU07_USE_DEBUG_SHADOWMAP + //--------------------------------------------------------------------------- TDynamicObject *Controlled = NULL; // pojazd, który prowadzimy @@ -632,8 +634,13 @@ void TWorld::OnKeyDown(int cKey) break; } case GLFW_KEY_F8: { +#ifdef EU07_USE_DEBUG_SHADOWMAP + if( Global::iTextMode == cKey ) { ++Global::iScreenMode[ cKey - GLFW_KEY_F1 ]; } + if( Global::iScreenMode[ cKey - GLFW_KEY_F1 ] > 1 ) { + Global::iScreenMode[ cKey - GLFW_KEY_F1 ] = 0; + } +#endif Global::iTextMode = cKey; - // FPS break; } case GLFW_KEY_F9: { @@ -1693,7 +1700,7 @@ TWorld::Update_UI() { std::to_string( int( tmp->MoverParameters->Im ) ) ) + "; U=" + to_string( int( tmp->MoverParameters->RunningTraction.TractionVoltage + 0.5 ) ) + "; R=" + - ( tmp->MoverParameters->RunningShape.R > 100000.0 ? + ( std::abs( tmp->MoverParameters->RunningShape.R ) > 10000.0 ? "~0.0" : to_string( tmp->MoverParameters->RunningShape.R, 1 ) ) + " An=" + to_string( tmp->MoverParameters->AccN, 2 ); // przyspieszenie poprzeczne diff --git a/openglmatrixstack.h b/openglmatrixstack.h index b57f545b..b75e37c7 100644 --- a/openglmatrixstack.h +++ b/openglmatrixstack.h @@ -55,6 +55,10 @@ public: multiply( glm::mat4 const &Matrix ) { m_stack.top() *= Matrix; upload(); } + void + ortho( float const Left, float const Right, float const Bottom, float const Top, float const Znear, float const Zfar ) { + m_stack.top() *= glm::ortho( Left, Right, Bottom, Top, Znear, Zfar ); + upload(); } void perspective( float const Fovy, float const Aspect, float const Znear, float const Zfar ) { m_stack.top() *= glm::perspective( Fovy, Aspect, Znear, Zfar ); @@ -132,10 +136,20 @@ public: m_stacks[ m_mode ].multiply( glm::make_mat4( Matrix ) ); } template + void + ortho( Type_ const Left, Type_ const Right, Type_ const Bottom, Type_ const Top, Type_ const Znear, Type_ const Zfar ) { + m_stacks[ m_mode ].ortho( + static_cast( Left ), + static_cast( Right ), + static_cast( Bottom ), + static_cast( Top ), + static_cast( Znear ), + static_cast( Zfar ) ); } + template void perspective( Type_ const Fovy, Type_ const Aspect, Type_ const Znear, Type_ const Zfar ) { m_stacks[ m_mode ].perspective( - static_cast(Fovy) * 0.0174532925f, // deg2rad + static_cast( glm::radians( Fovy ) ), static_cast( Aspect ), static_cast( Znear ), static_cast( Zfar ) ); } @@ -177,6 +191,7 @@ extern opengl_matrices OpenGLMatrices; // NOTE: no scale override as we aren't using it anywhere #define glMultMatrixd OpenGLMatrices.multiply #define glMultMatrixf OpenGLMatrices.multiply +#define glOrtho OpenGLMatrices.ortho #define gluPerspective OpenGLMatrices.perspective #define gluLookAt OpenGLMatrices.look_at diff --git a/renderer.cpp b/renderer.cpp index 07453165..c3ce5a93 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -25,9 +25,12 @@ http://mozilla.org/MPL/2.0/. opengl_renderer GfxRenderer; extern TWorld World; +//#define EU07_USE_ORTHO_SHADOWS + namespace colors { -glm::vec4 const none { 0.0f, 0.0f, 0.0f, 1.0f }; +glm::vec4 const none { 0.f, 0.f, 0.f, 1.f }; +glm::vec4 const white{ 1.f, 1.f, 1.f, 1.f }; } // namespace colors @@ -67,6 +70,10 @@ opengl_renderer::Init( GLFWwindow *Window ) { glEnable( GL_CULL_FACE ); // Cull back-facing triangles glShadeModel( GL_SMOOTH ); // Enable Smooth Shading + glActiveTexture( m_diffusetextureunit ); + glClientActiveTexture( m_diffusetextureunit ); + UILayer.set_unit( m_diffusetextureunit ); + glEnable( GL_DEPTH_TEST ); glAlphaFunc( GL_GREATER, 0.04f ); glEnable( GL_ALPHA_TEST ); @@ -152,22 +159,41 @@ opengl_renderer::Init( GLFWwindow *Window ) { ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 ); // switch back to primary render target for now } #endif + + ::glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + // preload some common textures + WriteLog( "Loading common gfx data..." ); + m_glaretexture = Fetch_Texture( "fx\\lightglare" ); + m_suntexture = Fetch_Texture( "fx\\sun" ); + m_moontexture = Fetch_Texture( "fx\\moon" ); + if( m_helpertextureunit >= 0 ) { + m_reflectiontexture = Fetch_Texture( "fx\\reflections" ); + } + WriteLog( "...gfx data pre-loading done" ); + // shadowmap resources if( ( true == Global::RenderShadows ) && ( true == m_framebuffersupport ) ) { // texture: ::glGenTextures( 1, &m_shadowtexture ); ::glBindTexture( GL_TEXTURE_2D, m_shadowtexture ); + // allocate memory + ::glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, m_shadowbuffersize, m_shadowbuffersize, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL ); + // setup parameters ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); - ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); - ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); // enable shadow comparison: true (ie not in shadow) if r<=texture... ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE ); ::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL ); - // ...shadow comparison should generate an INTENSITY result - ::glTexParameteri( GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY ); - ::glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, m_shadowbuffersize, m_shadowbuffersize, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL ); + ::glTexParameteri( GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE ); + // eye_linear is the default, so we could probably skip it + ::glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR ); + ::glTexGeni( GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR ); + ::glTexGeni( GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR ); + ::glTexGeni( GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR ); + ::glBindTexture( GL_TEXTURE_2D, 0 ); // create and assemble the framebuffer ::glGenFramebuffersEXT( 1, &m_shadowframebuffer ); ::glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, m_shadowframebuffer ); @@ -187,13 +213,6 @@ opengl_renderer::Init( GLFWwindow *Window ) { ::glDrawBuffer( GL_BACK ); } - ::glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); - // preload some common textures - WriteLog( "Loading common gfx data..." ); - m_glaretexture = Fetch_Texture( "fx\\lightglare" ); - m_suntexture = Fetch_Texture( "fx\\sun" ); - m_moontexture = Fetch_Texture( "fx\\moon" ); - WriteLog( "...gfx data pre-loading done" ); // prepare basic geometry chunks auto const geometrybank = m_geometry.create_bank(); float const size = 2.5f; @@ -217,6 +236,7 @@ opengl_renderer::Render() { auto const drawstart = std::chrono::steady_clock::now(); + m_renderpass.draw_mode = rendermode::none; // force setup anew Render_pass( rendermode::color ); glfwSwapBuffers( m_window ); @@ -248,7 +268,15 @@ opengl_renderer::Render_pass( rendermode const Mode ) { ::glDrawBuffer( GL_BACK ); } - ::glViewport( 0, 0, Global::ScreenWidth, Global::ScreenHeight ); + if( ( Global::iTextMode == GLFW_KEY_F8 ) && ( Global::iScreenMode[ GLFW_KEY_F8 - GLFW_KEY_F1 ] == 1 ) ) { + // debug shadowmap + ::glViewport( 0, 0, Global::ScreenHeight, Global::ScreenHeight ); + m_renderpass.draw_range = 1250.f; // 1.0km square centered around camera + } + else { + ::glViewport( 0, 0, Global::ScreenWidth, Global::ScreenHeight ); + m_renderpass.draw_range = 2500.0f; // arbitrary base draw range + } if( World.InitPerformed() ) { auto const skydomecolour = World.Environment.m_skydome.GetAverageColor(); @@ -261,19 +289,22 @@ opengl_renderer::Render_pass( rendermode const Mode ) { if( World.InitPerformed() ) { // setup - m_renderpass.draw_range = 2500.0f; // arbitrary base draw range - Render_projection(); - Render_camera(); + setup_projection(); + setup_camera(); ::glDepthFunc( GL_LEQUAL ); // render - Render_setup( true ); + setup_drawing( true ); + setup_units( true, false, false ); Render( &World.Environment ); // opaque parts... - Render_setup(); + setup_drawing( false ); + setup_units( true, true, true ); Render( &World.Ground ); // ...translucent parts - Render_setup( true ); + setup_drawing( true ); Render_Alpha( &World.Ground ); + // cab render is performed without shadows, due to low resolution and number of models without windows :| + toggle_units( true, false, false ); // cab render is done in translucent phase to deal with badly configured vehicles if( World.Train != nullptr ) { Render_cab( World.Train->Dynamic() ); } } @@ -283,21 +314,34 @@ opengl_renderer::Render_pass( rendermode const Mode ) { case rendermode::shadows: { - ::glViewport( 0, 0, m_shadowbuffersize, m_shadowbuffersize ); - - ::glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); - ::glClear( GL_DEPTH_BUFFER_BIT ); - if( World.InitPerformed() ) { // setup - m_renderpass.draw_range = 500.0f; // 1.0km square centered around camera - Render_projection(); - Render_camera(); + ::glViewport( 0, 0, m_shadowbuffersize, m_shadowbuffersize ); + + ::glClear( GL_DEPTH_BUFFER_BIT ); + ::glScissor( 1, 1, m_shadowbuffersize - 2, m_shadowbuffersize - 2 ); + ::glEnable( GL_SCISSOR_TEST ); + + m_renderpass.draw_range = 1250.f; // 1.0km square centered around camera + m_shadowtexturematrix = + glm::mat4{ + 0.5f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.5f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.5f, 0.0f, + 0.5f, 0.5f, 0.5f, 1.0f }; //bias from [-1, 1] to [0, 1] }; + setup_projection(); + setup_camera(); ::glDepthFunc( GL_LEQUAL ); + ::glEnable( GL_POLYGON_OFFSET_FILL ); // alleviate depth-fighting + ::glPolygonOffset( 4.f, 8.f ); // render // opaque parts... - Render_setup(); + setup_drawing( false ); + setup_units( false, false, false ); Render( &World.Ground ); + // post-render restore + ::glDisable( GL_POLYGON_OFFSET_FILL ); + ::glDisable( GL_SCISSOR_TEST ); } break; } @@ -307,19 +351,20 @@ opengl_renderer::Render_pass( rendermode const Mode ) { #ifdef EU07_USE_PICKING_FRAMEBUFFER ::glViewport( 0, 0, m_pickbuffersize, m_pickbuffersize ); #endif - ::glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); - ::glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); - if( World.InitPerformed() ) { // setup + ::glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); + ::glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + m_pickcontrolsitems.clear(); m_renderpass.draw_range = 50.0f; // doesn't really matter for control picking - Render_projection(); - Render_camera(); + setup_projection(); + setup_camera(); ::glDepthFunc( GL_LEQUAL ); // render // opaque parts... - Render_setup(); + setup_drawing( false ); + setup_units( false, false, false ); // cab render skips translucent parts, so we can do it here if( World.Train != nullptr ) { Render_cab( World.Train->Dynamic() ); } } @@ -332,19 +377,20 @@ opengl_renderer::Render_pass( rendermode const Mode ) { #ifdef EU07_USE_PICKING_FRAMEBUFFER ::glViewport( 0, 0, m_pickbuffersize, m_pickbuffersize ); #endif - ::glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); - ::glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); - if( World.InitPerformed() ) { // setup + ::glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); + ::glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + m_picksceneryitems.clear(); m_renderpass.draw_range = 1000.0f; // scenery picking is likely to focus on nearby nodes - Render_projection(); - Render_camera(); + setup_projection(); + setup_camera(); ::glDepthFunc( GL_LEQUAL ); // render // opaque parts... - Render_setup(); + setup_drawing( false ); + setup_units( false, false, false ); Render( &World.Ground ); } break; @@ -354,12 +400,11 @@ opengl_renderer::Render_pass( rendermode const Mode ) { break; } } - } // configures projection matrix for the current render pass void -opengl_renderer::Render_projection() { +opengl_renderer::setup_projection() { ::glMatrixMode( GL_PROJECTION ); ::glLoadIdentity(); @@ -371,11 +416,17 @@ opengl_renderer::Render_projection() { case rendermode::pickscenery: #endif case rendermode::color: { - ::gluPerspective( - Global::FieldOfView / Global::ZoomFactor, - std::max( 1.0f, (float)Global::ScreenWidth ) / std::max( 1.0f, (float)Global::ScreenHeight ), - 0.1f * Global::ZoomFactor, - m_renderpass.draw_range * Global::fDistanceFactor ); + if( ( Global::iTextMode == GLFW_KEY_F8 ) && ( Global::iScreenMode[ GLFW_KEY_F8 - GLFW_KEY_F1 ] == 1 ) ) { + // debug shadowmap +#ifdef EU07_USE_ORTHO_SHADOWS + setup_projection_light_ortho(); +#else + setup_projection_light_perspective(); +#endif + } + else { + setup_projection_world(); + } break; } #ifdef EU07_USE_PICKING_FRAMEBUFFER @@ -393,13 +444,12 @@ opengl_renderer::Render_projection() { } #endif case rendermode::shadows: { - // TODO: set parallel projection - ::gluPerspective( - Global::FieldOfView, - 1.f, -// std::max( 1.0f, (float)Global::ScreenWidth ) / std::max( 1.0f, (float)Global::ScreenHeight ), - 2.f, - m_renderpass.draw_range * Global::fDistanceFactor ); +#ifdef EU07_USE_ORTHO_SHADOWS + setup_projection_light_ortho(); +#else + setup_projection_light_perspective(); +#endif + m_shadowtexturematrix *= OpenGLMatrices.data( GL_PROJECTION ); break; } @@ -409,9 +459,38 @@ opengl_renderer::Render_projection() { } } +void +opengl_renderer::setup_projection_world() { + + ::gluPerspective( + Global::FieldOfView / Global::ZoomFactor, + std::max( 1.f, (float)Global::ScreenWidth ) / std::max( 1.f, (float)Global::ScreenHeight ), + 0.1f * Global::ZoomFactor, + m_renderpass.draw_range * Global::fDistanceFactor ); +} + +void +opengl_renderer::setup_projection_light_ortho() { + + ::glOrtho( + -250, 250, + -250, 250, + -2500, 2500 ); +} + +void +opengl_renderer::setup_projection_light_perspective() { + + ::gluPerspective( + 45.f, + 1.f, + 100.f, // light source is pulled back far enough we won't likely have anything too close to it, can get some z-range here + m_renderpass.draw_range * Global::fDistanceFactor ); +} + // configures modelview matrix for the current render pass void -opengl_renderer::Render_camera() { +opengl_renderer::setup_camera() { ::glMatrixMode( GL_MODELVIEW ); // Select The Modelview Matrix ::glLoadIdentity(); @@ -422,17 +501,29 @@ opengl_renderer::Render_camera() { case rendermode::color: case rendermode::pickcontrols: case rendermode::pickscenery: { - World.Camera.SetMatrix( viewmatrix ); - m_renderpass.camera.position() = Global::pCameraPosition; + if( ( Global::iTextMode == GLFW_KEY_F8 ) && ( Global::iScreenMode[ GLFW_KEY_F8 - GLFW_KEY_F1 ] == 1 ) ) { +#ifdef EU07_USE_ORTHO_SHADOWS + setup_camera_light_ortho( viewmatrix ); +#else + setup_camera_light_perspective( viewmatrix ); +#endif + } + else { + setup_camera_world( viewmatrix ); + } break; } case rendermode::shadows: { - m_renderpass.camera.position() = Global::pCameraPosition - glm::dvec3{ Global::DayLight.direction * 250.f }; - m_renderpass.camera.position().y = std::max( 50.0, m_renderpass.camera.position().y ); // prevent shadow source from dipping too low - viewmatrix = glm::lookAt( - m_renderpass.camera.position(), - glm::dvec3{ Global::pCameraPosition.x, 0.0, Global::pCameraPosition.z }, - glm::dvec3{ 0.f, 1.f, 0.f } ); +#ifdef EU07_USE_ORTHO_SHADOWS + setup_camera_light_ortho( viewmatrix ); +#else + setup_camera_light_perspective( viewmatrix ); +#endif + // during colour pass coordinates are moved from camera-centric to light-centric, essentially the difference between these two origins + m_shadowtexturematrix *= + glm::translate( + glm::mat4{ glm::dmat3{ viewmatrix } }, + glm::vec3{ glm::dvec3{ Global::pCameraPosition } - m_renderpass.camera.position() } ); break; } default: { @@ -445,7 +536,45 @@ opengl_renderer::Render_camera() { } void -opengl_renderer::Render_setup( bool const Alpha ) { +opengl_renderer::setup_camera_world( glm::dmat4 &Viewmatrix ) { + + World.Camera.SetMatrix( Viewmatrix ); + m_renderpass.camera.position() = Global::pCameraPosition; +} + +void +opengl_renderer::setup_camera_light_ortho( glm::dmat4 &Viewmatrix ) { +/* + m_renderpass.camera.position() = Global::pCameraPosition - glm::dvec3{ Global::DayLight.direction * 250.f }; + m_renderpass.camera.position().y = std::max( 75.0, m_renderpass.camera.position().y ); // prevent shadow source from dipping too low + Viewmatrix = glm::lookAt( + m_renderpass.camera.position(), + glm::dvec3{ Global::pCameraPosition.x, 0.0, Global::pCameraPosition.z }, + glm::dvec3{ 0.f, 1.f, 0.f } ); +*/ + m_renderpass.camera.position() = Global::pCameraPosition - glm::dvec3{ Global::DayLight.direction }; + if( m_renderpass.camera.position().y - Global::pCameraPosition.y < 0.15 ) { + m_renderpass.camera.position().y = Global::pCameraPosition.y + 0.15; + } + Viewmatrix = glm::lookAt( + m_renderpass.camera.position(), + glm::dvec3{ Global::pCameraPosition }, + glm::dvec3{ 0.f, 1.f, 0.f } ); +} + +void +opengl_renderer::setup_camera_light_perspective( glm::dmat4 &Viewmatrix ) { + + m_renderpass.camera.position() = Global::pCameraPosition - glm::dvec3{ Global::DayLight.direction * m_renderpass.draw_range * 0.5f }; + m_renderpass.camera.position().y = std::max( 75.0, m_renderpass.camera.position().y ); // prevent shadow source from dipping too low + Viewmatrix = glm::lookAt( + m_renderpass.camera.position(), + glm::dvec3{ Global::pCameraPosition.x, 0.0, Global::pCameraPosition.z }, + glm::dvec3{ 0.f, 1.f, 0.f } ); +} + +void +opengl_renderer::setup_drawing( bool const Alpha ) { if( true == Alpha ) { @@ -473,19 +602,6 @@ opengl_renderer::Render_setup( bool const Alpha ) { } else { ::glDisable( GL_FOG ); } - if( m_texenvmode != m_renderpass.draw_mode ) { - // texture colour and alpha - ::glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR ); -/* - ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA ); -*/ - m_texenvmode = m_renderpass.draw_mode; - } break; } case rendermode::shadows: @@ -498,19 +614,6 @@ opengl_renderer::Render_setup( bool const Alpha ) { } ::glDisable( GL_FOG ); - if( m_texenvmode != m_renderpass.draw_mode ) { - // solid colour with texture alpha - ::glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PRIMARY_COLOR ); // or GL_PREVIOUS - ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR ); -/* - ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE ); - ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA ); -*/ - m_texenvmode = m_renderpass.draw_mode; - } break; } default: { @@ -519,6 +622,169 @@ opengl_renderer::Render_setup( bool const Alpha ) { } } +// configures, enables and disables specified texture units +void +opengl_renderer::setup_units( bool const Diffuse, bool const Shadows, bool const Reflections ) { + // helper texture unit. + // darkens previous stage, preparing data for the shadow texture unit to select from + if( m_helpertextureunit >= 0 ) { + if( ( true == Global::RenderShadows ) && ( true == Shadows ) ) { + // setup reflection texture unit + ::glActiveTexture( m_helpertextureunit ); + ::glBindTexture( GL_TEXTURE_2D, m_reflectiontexture ); // TODO: move to reflection unit setup + ::glEnable( GL_TEXTURE_2D ); +/* + glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP ); + glTexGeni( GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP ); + glEnable( GL_TEXTURE_GEN_S ); + glEnable( GL_TEXTURE_GEN_T ); +*/ + ::glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); + ::glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, glm::value_ptr( m_shadowcolor ) ); // TODO: dynamically calculated shadow colour, based on sun height + + ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE ); // darken the previous stage + ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR ); + + ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE ); // simply copy alpha from diffuse stage + ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS ); + } + else { + ::glActiveTexture( m_helpertextureunit ); + ::glDisable( GL_TEXTURE_2D ); +/* + ::glDisable( GL_TEXTURE_GEN_S ); + ::glDisable( GL_TEXTURE_GEN_T ); + ::glDisable( GL_TEXTURE_GEN_R ); + ::glDisable( GL_TEXTURE_GEN_Q ); +*/ + } + } + // shadow texture unit. + // interpolates between primary colour and the previous unit, which should hold darkened variant of the primary colour + if( m_shadowtextureunit >= 0 ) { + if( ( true == Global::RenderShadows ) && ( true == Shadows ) ) { + + ::glActiveTexture( m_shadowtextureunit ); + ::glBindTexture( GL_TEXTURE_2D, m_shadowtexture ); + ::glEnable( GL_TEXTURE_2D ); + // s + ::glTexGenfv( GL_S, GL_EYE_PLANE, glm::value_ptr( glm::row( m_shadowtexturematrix, 0 ) ) ); + ::glEnable( GL_TEXTURE_GEN_S ); + // t + ::glTexGenfv( GL_T, GL_EYE_PLANE, glm::value_ptr( glm::row( m_shadowtexturematrix, 1 ) ) ); + ::glEnable( GL_TEXTURE_GEN_T ); + // r + ::glTexGenfv( GL_R, GL_EYE_PLANE, glm::value_ptr( glm::row( m_shadowtexturematrix, 2 ) ) ); + ::glEnable( GL_TEXTURE_GEN_R ); + // q + ::glTexGenfv( GL_Q, GL_EYE_PLANE, glm::value_ptr( glm::row( m_shadowtexturematrix, 3 ) ) ); + ::glEnable( GL_TEXTURE_GEN_Q ); + + ::glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE ); // choose between lit and lit * shadow colour, based on depth test + ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PRIMARY_COLOR ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_TEXTURE ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_COLOR ); + + ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE ); // simply copy alpha from diffuse stage + ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS ); + } + else { + // turn off shadow map tests + ::glActiveTexture( m_shadowtextureunit ); + + ::glDisable( GL_TEXTURE_2D ); + ::glDisable( GL_TEXTURE_GEN_S ); + ::glDisable( GL_TEXTURE_GEN_T ); + ::glDisable( GL_TEXTURE_GEN_R ); + ::glDisable( GL_TEXTURE_GEN_Q ); + } + } + // diffuse texture unit. + // NOTE: diffuse texture mapping is never fully disabled, alpha channel information is always included + ::glActiveTexture( m_diffusetextureunit ); + if( true == Diffuse ) { + // default behaviour, modulate with previous stage + ::glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR ); +/* + ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA ); +*/ + } + else { + // solid colour with texture alpha + ::glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR ); +/* + ::glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE ); + ::glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA ); +*/ + } +} + +// enables and disables specified texture units +void +opengl_renderer::toggle_units( bool const Diffuse, bool const Shadows, bool const Reflections ) { + // helper texture unit. + if( m_helpertextureunit >= 0 ) { + if( ( true == Global::RenderShadows ) && ( true == Shadows ) ) { + ::glActiveTexture( m_helpertextureunit ); + ::glEnable( GL_TEXTURE_2D ); + } + else { + ::glActiveTexture( m_helpertextureunit ); + ::glDisable( GL_TEXTURE_2D ); + } + } + // shadow texture unit. + if( m_shadowtextureunit >= 0 ) { + if( ( true == Global::RenderShadows ) && ( true == Shadows ) ) { + + ::glActiveTexture( m_shadowtextureunit ); + ::glEnable( GL_TEXTURE_2D ); + } + else { + + ::glActiveTexture( m_shadowtextureunit ); + ::glDisable( GL_TEXTURE_2D ); + } + } + // diffuse texture unit. + // NOTE: toggle actually disables diffuse texture mapping, unlike setup counterpart + if( true == Diffuse ) { + + ::glActiveTexture( m_diffusetextureunit ); + ::glEnable( GL_TEXTURE_2D ); + } + else { + + ::glActiveTexture( m_diffusetextureunit ); + ::glDisable( GL_TEXTURE_2D ); + } +} + +void +opengl_renderer::setup_shadow_color( glm::vec4 const &Shadowcolor ) { + + ::glActiveTexture( m_helpertextureunit ); + ::glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, glm::value_ptr( Shadowcolor ) ); // in-shadow colour multiplier + ::glActiveTexture( m_diffusetextureunit ); + +} + bool opengl_renderer::Render( world_environment *Environment ) { @@ -587,10 +853,10 @@ opengl_renderer::Render( world_environment *Environment ) { float const size = 0.045f; ::glBegin( GL_TRIANGLE_STRIP ); - ::glTexCoord2f( 1.0f, 1.0f ); ::glVertex3f( -size, size, 0.0f ); - ::glTexCoord2f( 1.0f, 0.0f ); ::glVertex3f( -size, -size, 0.0f ); - ::glTexCoord2f( 0.0f, 1.0f ); ::glVertex3f( size, size, 0.0f ); - ::glTexCoord2f( 0.0f, 0.0f ); ::glVertex3f( size, -size, 0.0f ); + ::glMultiTexCoord2f( m_diffusetextureunit, 1.f, 1.f ); ::glVertex3f( -size, size, 0.f ); + ::glMultiTexCoord2f( m_diffusetextureunit, 1.f, 0.f ); ::glVertex3f( -size, -size, 0.f ); + ::glMultiTexCoord2f( m_diffusetextureunit, 0.f, 1.f ); ::glVertex3f( size, size, 0.f ); + ::glMultiTexCoord2f( m_diffusetextureunit, 0.f, 0.f ); ::glVertex3f( size, -size, 0.f ); ::glEnd(); ::glPopMatrix(); @@ -623,10 +889,10 @@ opengl_renderer::Render( world_environment *Environment ) { else { moonv = 1.0f - 0.0f; moonu = 0.0f; } ::glBegin( GL_TRIANGLE_STRIP ); - ::glTexCoord2f( moonu, moonv ); ::glVertex3f( -size, size, 0.0f ); - ::glTexCoord2f( moonu, moonv - 0.333f ); ::glVertex3f( -size, -size, 0.0f ); - ::glTexCoord2f( moonu + 0.333f, moonv ); ::glVertex3f( size, size, 0.0f ); - ::glTexCoord2f( moonu + 0.333f, moonv - 0.333f ); ::glVertex3f( size, -size, 0.0f ); + ::glMultiTexCoord2f( m_diffusetextureunit, moonu, moonv ); ::glVertex3f( -size, size, 0.0f ); + ::glMultiTexCoord2f( m_diffusetextureunit, moonu, moonv - 0.333f ); ::glVertex3f( -size, -size, 0.0f ); + ::glMultiTexCoord2f( m_diffusetextureunit, moonu + 0.333f, moonv ); ::glVertex3f( size, size, 0.0f ); + ::glMultiTexCoord2f( m_diffusetextureunit, moonu + 0.333f, moonv - 0.333f ); ::glVertex3f( size, -size, 0.0f ); ::glEnd(); ::glPopMatrix(); @@ -831,21 +1097,23 @@ opengl_renderer::Render( TGroundRect *Groundcell ) { case rendermode::pickscenery: { // non-interactive scenery elements get neutral colour ::glColor3fv( glm::value_ptr( colors::none ) ); - break; } case rendermode::shadows: - case rendermode::color: + case rendermode::color: { + if( Groundcell->nRenderRect != nullptr ) { + // nieprzezroczyste trójkÄ…ty kwadratu kilometrowego + for( TGroundNode *node = Groundcell->nRenderRect; node != nullptr; node = node->nNext3 ) { + Render( node ); + } + } + break; + } case rendermode::pickcontrols: default: { break; } } - if( Groundcell->nRenderRect != nullptr ) { - // nieprzezroczyste trójkÄ…ty kwadratu kilometrowego - for( TGroundNode *node = Groundcell->nRenderRect; node != nullptr; node = node->nNext3 ) { - Render( node ); - } - } + if( Groundcell->nTerrain ) { Render( Groundcell->nTerrain ); @@ -969,7 +1237,18 @@ opengl_renderer::Render( TGroundNode *Node ) { return true; } - double const distancesquared = SquareMagnitude( ( Node->pCenter - m_renderpass.camera.position() ) / Global::ZoomFactor ); + double distancesquared; + switch( m_renderpass.draw_mode ) { + case rendermode::shadows: { + // 'camera' for the light pass is the light source, but we need distance from actual camera + distancesquared = SquareMagnitude( ( Node->pCenter - Global::pCameraPosition ) / Global::ZoomFactor ); + break; + } + default: { + distancesquared = SquareMagnitude( ( Node->pCenter - m_renderpass.camera.position() ) / Global::ZoomFactor ); + break; + } + } if( ( distancesquared > ( Node->fSquareRadius * Global::fDistanceFactor ) ) || ( distancesquared < ( Node->fSquareMinRadius / Global::fDistanceFactor ) ) ) { return false; @@ -1015,8 +1294,30 @@ opengl_renderer::Render( TGroundNode *Node ) { #else Node->Model->RaAnimate(); // jednorazowe przeliczenie animacji Node->Model->RaPrepare(); - if( Node->Model->pModel ) // renderowanie rekurencyjne submodeli - Render( Node->Model->pModel, Node->Model->Material(), Node->pCenter - m_renderpass.camera.position(), Node->Model->vAngle ); + if( Node->Model->pModel ) { + // renderowanie rekurencyjne submodeli + switch( m_renderpass.draw_mode ) { + case rendermode::shadows: { + Render( + Node->Model->pModel, + Node->Model->Material(), + SquareMagnitude( Node->pCenter - Global::pCameraPosition ), + Node->pCenter - m_renderpass.camera.position(), + Node->Model->vAngle ); + break; + } + default: { + auto const position = Node->pCenter - m_renderpass.camera.position(); + Render( + Node->Model->pModel, + Node->Model->Material(), + SquareMagnitude( position ), + position, + Node->Model->vAngle ); + break; + } + } + } #endif return true; } @@ -1154,11 +1455,20 @@ opengl_renderer::Render( TDynamicObject *Dynamic ) { if( false == Dynamic->renderme ) { return false; } - // setup TSubModel::iInstance = ( size_t )this; //żeby nie robić cudzych animacji auto const originoffset = Dynamic->vPosition - m_renderpass.camera.position(); - double const squaredistance = SquareMagnitude( originoffset / Global::ZoomFactor ); + double squaredistance; + switch( m_renderpass.draw_mode ) { + case rendermode::shadows: { + squaredistance = SquareMagnitude( ( Dynamic->vPosition - Global::pCameraPosition ) / Global::ZoomFactor ); + break; + } + default: { + squaredistance = SquareMagnitude( originoffset / Global::ZoomFactor ); + break; + } + } Dynamic->ABuLittleUpdate( squaredistance ); // ustawianie zmiennych submodeli dla wspólnego modelu ::glPushMatrix(); @@ -1342,7 +1652,7 @@ opengl_renderer::Render( TModel3d *Model, material_data const *Material, double } bool -opengl_renderer::Render( TModel3d *Model, material_data const *Material, Math3D::vector3 const &Position, Math3D::vector3 const &Angle ) { +opengl_renderer::Render( TModel3d *Model, material_data const *Material, double const Squaredistance, Math3D::vector3 const &Position, Math3D::vector3 const &Angle ) { ::glPushMatrix(); ::glTranslated( Position.x, Position.y, Position.z ); @@ -1353,7 +1663,7 @@ opengl_renderer::Render( TModel3d *Model, material_data const *Material, Math3D: if( Angle.z != 0.0 ) ::glRotated( Angle.z, 0.0, 0.0, 1.0 ); - auto const result = Render( Model, Material, SquareMagnitude( Position ) ); // position is effectively camera offset + auto const result = Render( Model, Material, Squaredistance ); ::glPopMatrix(); @@ -1417,6 +1727,8 @@ opengl_renderer::Render( TSubModel *Submodel ) { if( Global::fLuminance < Submodel->fLight ) { // zeby swiecilo na kolorowo ::glMaterialfv( GL_FRONT, GL_EMISSION, glm::value_ptr( Submodel->f4Diffuse * Submodel->f4Emision.a ) ); + // disable shadows so they don't obstruct self-lit items + setup_shadow_color( colors::white ); } // main draw call @@ -1429,6 +1741,7 @@ opengl_renderer::Render( TSubModel *Submodel ) { if( Global::fLuminance < Submodel->fLight ) { // restore default (lack of) brightness ::glMaterialfv( GL_FRONT, GL_EMISSION, glm::value_ptr( colors::none ) ); + setup_shadow_color( m_shadowcolor ); } #ifdef EU07_USE_OPTIMIZED_NORMALIZATION switch( Submodel->m_normalizenormals ) { @@ -1524,10 +1837,14 @@ opengl_renderer::Render( TSubModel *Submodel ) { ::glLoadIdentity(); ::glTranslatef( lightcenter.x, lightcenter.y, lightcenter.z ); // poczÄ…tek ukÅ‚adu zostaje bez zmian + setup_shadow_color( colors::white ); + // main draw call m_geometry.draw( Submodel->m_geometry ); // post-draw reset + // re-enable shadows + setup_shadow_color( m_shadowcolor ); ::glPopMatrix(); ::glPopAttrib(); } @@ -1709,7 +2026,18 @@ opengl_renderer::Render_Alpha( TSubRect *Groundsubcell ) { bool opengl_renderer::Render_Alpha( TGroundNode *Node ) { - double const distancesquared = SquareMagnitude( ( Node->pCenter - m_renderpass.camera.position() ) / Global::ZoomFactor ); + double distancesquared; + switch( m_renderpass.draw_mode ) { + case rendermode::shadows: { + // 'camera' for the light pass is the light source, but we need distance from actual camera + distancesquared = SquareMagnitude( ( Node->pCenter - Global::pCameraPosition ) / Global::ZoomFactor ); + break; + } + default: { + distancesquared = SquareMagnitude( ( Node->pCenter - m_renderpass.camera.position() ) / Global::ZoomFactor ); + break; + } + } if( ( distancesquared > ( Node->fSquareRadius * Global::fDistanceFactor ) ) || ( distancesquared < ( Node->fSquareMinRadius / Global::fDistanceFactor ) ) ) { return false; @@ -1766,8 +2094,30 @@ opengl_renderer::Render_Alpha( TGroundNode *Node ) { Node->Model->RenderAlpha( Node->pCenter - m_renderpass.camera.position() ); #else Node->Model->RaPrepare(); - if( Node->Model->pModel ) // renderowanie rekurencyjne submodeli - Render_Alpha( Node->Model->pModel, Node->Model->Material(), Node->pCenter - m_renderpass.camera.position(), Node->Model->vAngle ); + if( Node->Model->pModel ) { + // renderowanie rekurencyjne submodeli + switch( m_renderpass.draw_mode ) { + case rendermode::shadows: { + Render_Alpha( + Node->Model->pModel, + Node->Model->Material(), + SquareMagnitude( Node->pCenter - Global::pCameraPosition ), + Node->pCenter - m_renderpass.camera.position(), + Node->Model->vAngle ); + break; + } + default: { + auto const position = Node->pCenter - m_renderpass.camera.position(); + Render_Alpha( + Node->Model->pModel, + Node->Model->Material(), + SquareMagnitude( position ), + position, + Node->Model->vAngle ); + break; + } + } + } #endif return true; } @@ -1848,7 +2198,17 @@ opengl_renderer::Render_Alpha( TDynamicObject *Dynamic ) { // setup TSubModel::iInstance = ( size_t )this; //żeby nie robić cudzych animacji auto const originoffset = Dynamic->vPosition - m_renderpass.camera.position(); - double const squaredistance = SquareMagnitude( originoffset / Global::ZoomFactor ); + double squaredistance; + switch( m_renderpass.draw_mode ) { + case rendermode::shadows: { + squaredistance = SquareMagnitude( ( Dynamic->vPosition - Global::pCameraPosition ) / Global::ZoomFactor ); + break; + } + default: { + squaredistance = SquareMagnitude( originoffset / Global::ZoomFactor ); + break; + } + } Dynamic->ABuLittleUpdate( squaredistance ); // ustawianie zmiennych submodeli dla wspólnego modelu ::glPushMatrix(); @@ -1936,7 +2296,7 @@ opengl_renderer::Render_Alpha( TModel3d *Model, material_data const *Material, d } bool -opengl_renderer::Render_Alpha( TModel3d *Model, material_data const *Material, Math3D::vector3 const &Position, Math3D::vector3 const &Angle ) { +opengl_renderer::Render_Alpha( TModel3d *Model, material_data const *Material, double const Squaredistance, Math3D::vector3 const &Position, Math3D::vector3 const &Angle ) { ::glPushMatrix(); ::glTranslated( Position.x, Position.y, Position.z ); @@ -1947,7 +2307,7 @@ opengl_renderer::Render_Alpha( TModel3d *Model, material_data const *Material, M if( Angle.z != 0.0 ) ::glRotated( Angle.z, 0.0, 0.0, 1.0 ); - auto const result = Render_Alpha( Model, Material, SquareMagnitude( Position ) ); // position is effectively camera offset + auto const result = Render_Alpha( Model, Material, Squaredistance ); // position is effectively camera offset ::glPopMatrix(); @@ -2006,6 +2366,8 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { if( Global::fLuminance < Submodel->fLight ) { // zeby swiecilo na kolorowo ::glMaterialfv( GL_FRONT, GL_EMISSION, glm::value_ptr( Submodel->f4Diffuse * Submodel->f4Emision.a ) ); + // disable shadows so they don't obstruct self-lit items + setup_shadow_color( colors::white ); } // main draw call @@ -2018,6 +2380,7 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { if( Global::fLuminance < Submodel->fLight ) { // restore default (lack of) brightness ::glMaterialfv( GL_FRONT, GL_EMISSION, glm::value_ptr( colors::none ) ); + setup_shadow_color( m_shadowcolor ); } #ifdef EU07_USE_OPTIMIZED_NORMALIZATION switch( Submodel->m_normalizenormals ) { @@ -2068,6 +2431,8 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { ::glLoadIdentity(); // macierz jedynkowa ::glTranslatef( lightcenter.x, lightcenter.y, lightcenter.z ); // poczÄ…tek ukÅ‚adu zostaje bez zmian ::glRotated( std::atan2( lightcenter.x, lightcenter.z ) * 180.0 / M_PI, 0.0, 1.0, 0.0 ); // jedynie obracamy w pionie o kÄ…t + // disable shadows so they don't obstruct self-lit items + setup_shadow_color( colors::white ); // main draw call m_geometry.draw( m_billboardgeometry ); @@ -2080,6 +2445,8 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { // ...etc instead IF we had easy access to camera's forward and right vectors. TODO: check if Camera matrix is accessible */ // post-render cleanup + setup_shadow_color( m_shadowcolor ); + ::glPopMatrix(); ::glPopAttrib(); } @@ -2293,7 +2660,7 @@ opengl_renderer::Update( double const Deltatime ) { }; // debug performance string -std::string const & +std::string opengl_renderer::Info() const { return m_debuginfo; @@ -2397,15 +2764,36 @@ opengl_renderer::Init_caps() { else { WriteLog( "Framebuffer objects not supported, resorting to back buffer rendering where possible" ); } - if( Global::iMultisampling ) - WriteLog( "Using multisampling x" + std::to_string( 1 << Global::iMultisampling ) ); // ograniczenie maksymalnego rozmiaru tekstur - parametr dla skalowania tekstur { - GLint i; - glGetIntegerv( GL_MAX_TEXTURE_SIZE, &i ); - if( i < Global::iMaxTextureSize ) - Global::iMaxTextureSize = i; + GLint texturesize; + ::glGetIntegerv( GL_MAX_TEXTURE_SIZE, &texturesize ); + Global::iMaxTextureSize = std::min( Global::iMaxTextureSize, texturesize ); WriteLog( "Texture sizes capped at " + std::to_string( Global::iMaxTextureSize ) + " pixels" ); + m_shadowbuffersize = Global::shadowtune.map_size; + m_shadowbuffersize = std::min( m_shadowbuffersize, texturesize ); + WriteLog( "Shadows map size capped at " + std::to_string( m_shadowbuffersize ) + " pixels" ); + } + // cap the number of supported lights based on hardware + { + GLint maxlights; + ::glGetIntegerv( GL_MAX_LIGHTS, &maxlights ); + Global::DynamicLightCount = std::min( Global::DynamicLightCount, maxlights - 1 ); + WriteLog( "Dynamic light amount capped at " + std::to_string( Global::DynamicLightCount ) + " (" + std::to_string(maxlights) + " lights total supported by the gfx card)" ); + } + { + GLint maxtextureunits; + ::glGetIntegerv( GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxtextureunits ); + if( maxtextureunits < 4 ) { + WriteLog( "Less than 4 texture units, shadow and reflection mapping will be disabled" ); + Global::RenderShadows = false; + m_diffusetextureunit = GL_TEXTURE0; + m_shadowtextureunit = -1; + m_helpertextureunit = -1; + } + } + if( Global::iMultisampling ) { + WriteLog( "Using multisampling x" + std::to_string( 1 << Global::iMultisampling ) ); } return true; diff --git a/renderer.h b/renderer.h index 8095a188..8aab1794 100644 --- a/renderer.h +++ b/renderer.h @@ -159,7 +159,7 @@ public: TGroundNode const * Update_Pick_Node(); // debug performance string - std::string const & + std::string Info() const; // members @@ -169,6 +169,7 @@ public: private: // types enum class rendermode { + none, color, shadows, pickcontrols, @@ -194,12 +195,30 @@ private: Render_pass( rendermode const Mode ); // configures projection matrix for the current render pass void - Render_projection(); + setup_projection(); + void + setup_projection_world(); + void + setup_projection_light_ortho(); + void + setup_projection_light_perspective(); // configures camera for the current render pass void - Render_camera(); + setup_camera(); void - Render_setup( bool const Alpha = false ); + setup_camera_world( glm::dmat4 &Viewmatrix ); + void + setup_camera_light_ortho( glm::dmat4 &Viewmatrix ); + void + setup_camera_light_perspective( glm::dmat4 &Viewmatrix ); + void + setup_drawing( bool const Alpha = false ); + void + setup_units( bool const Diffuse, bool const Shadows, bool const Reflections ); + void + setup_shadow_color( glm::vec4 const &Shadowcolor ); + void + toggle_units( bool const Diffuse, bool const Shadows, bool const Reflections ); bool Render( world_environment *Environment ); bool @@ -213,7 +232,7 @@ private: bool Render( TDynamicObject *Dynamic ); bool - Render( TModel3d *Model, material_data const *Material, Math3D::vector3 const &Position, Math3D::vector3 const &Angle ); + Render( TModel3d *Model, material_data const *Material, double const Squaredistance, Math3D::vector3 const &Position, Math3D::vector3 const &Angle ); bool Render( TModel3d *Model, material_data const *Material, double const Squaredistance ); void @@ -233,7 +252,7 @@ private: bool Render_Alpha( TDynamicObject *Dynamic ); bool - Render_Alpha( TModel3d *Model, material_data const *Material, Math3D::vector3 const &Position, Math3D::vector3 const &Angle ); + Render_Alpha( TModel3d *Model, material_data const *Material, double const Squaredistance, Math3D::vector3 const &Position, Math3D::vector3 const &Angle ); bool Render_Alpha( TModel3d *Model, material_data const *Material, double const Squaredistance ); void @@ -257,13 +276,19 @@ private: #endif GLuint m_shadowframebuffer { 0 }; GLuint m_shadowtexture { 0 }; - int m_shadowbuffersize { 2048 }; + int m_shadowbuffersize { 4096 }; + glm::mat4 m_shadowtexturematrix; + glm::vec4 m_shadowcolor{ 0.5f, 0.5f, 0.5f, 1.f }; GLUquadricObj *m_quadric { nullptr }; // helper object for drawing debug mode scene elements + int m_shadowtextureunit { GL_TEXTURE1 }; + int m_helpertextureunit { GL_TEXTURE0 }; + int m_diffusetextureunit { GL_TEXTURE2 }; geometry_handle m_billboardgeometry { 0, 0 }; texture_handle m_glaretexture { -1 }; texture_handle m_suntexture { -1 }; texture_handle m_moontexture { -1 }; + texture_handle m_reflectiontexture { -1 }; float m_drawtime { 1000.0f / 30.0f * 20.0f }; // start with presumed 'neutral' average of 30 fps double m_updateaccumulator { 0.0 }; @@ -274,9 +299,7 @@ private: float m_speculartranslucentscalefactor { 1.0f }; bool m_framebuffersupport { false }; - rendermode m_texenvmode { rendermode::color }; // last configured texture environment renderpass_config m_renderpass; -// std::stack m_renderpasses; bool m_renderspecular { false }; // controls whether to include specular component in the calculations std::vector m_picksceneryitems; std::vector m_pickcontrolsitems; diff --git a/uilayer.cpp b/uilayer.cpp index 0b1474f9..d3ee278e 100644 --- a/uilayer.cpp +++ b/uilayer.cpp @@ -65,7 +65,7 @@ ui_layer::render() { glMatrixMode(GL_PROJECTION); glLoadIdentity(); - glOrtho( 0.f, Global::ScreenWidth, Global::ScreenHeight, 0.f, -1.f, 1.f ); + glOrtho( 0, Global::ScreenWidth, Global::ScreenHeight, 0, -1, 1 ); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); @@ -272,10 +272,10 @@ ui_layer::quad( float4 const &Coordinates, float4 const &Color ) { glBegin( GL_TRIANGLE_STRIP ); - glTexCoord2f( 0.f, 1.f ); glVertex2f( 0.5f * ( Global::iWindowWidth - width ) + Coordinates.x * heightratio, 0.5f * ( Global::iWindowHeight - height ) + Coordinates.y * heightratio ); - glTexCoord2f( 0.f, 0.f ); glVertex2f( 0.5f * ( Global::iWindowWidth - width ) + Coordinates.x * heightratio, 0.5f * ( Global::iWindowHeight - height ) + Coordinates.w * heightratio ); - glTexCoord2f( 1.f, 1.f ); glVertex2f( 0.5f * ( Global::iWindowWidth - width ) + Coordinates.z * heightratio, 0.5f * ( Global::iWindowHeight - height ) + Coordinates.y * heightratio ); - glTexCoord2f( 1.f, 0.f ); glVertex2f( 0.5f * ( Global::iWindowWidth - width ) + Coordinates.z * heightratio, 0.5f * ( Global::iWindowHeight - height ) + Coordinates.w * heightratio ); + glMultiTexCoord2f( m_textureunit, 0.f, 1.f ); glVertex2f( 0.5f * ( Global::iWindowWidth - width ) + Coordinates.x * heightratio, 0.5f * ( Global::iWindowHeight - height ) + Coordinates.y * heightratio ); + glMultiTexCoord2f( m_textureunit, 0.f, 0.f ); glVertex2f( 0.5f * ( Global::iWindowWidth - width ) + Coordinates.x * heightratio, 0.5f * ( Global::iWindowHeight - height ) + Coordinates.w * heightratio ); + glMultiTexCoord2f( m_textureunit, 1.f, 1.f ); glVertex2f( 0.5f * ( Global::iWindowWidth - width ) + Coordinates.z * heightratio, 0.5f * ( Global::iWindowHeight - height ) + Coordinates.y * heightratio ); + glMultiTexCoord2f( m_textureunit, 1.f, 0.f ); glVertex2f( 0.5f * ( Global::iWindowWidth - width ) + Coordinates.z * heightratio, 0.5f * ( Global::iWindowHeight - height ) + Coordinates.w * heightratio ); glEnd(); } diff --git a/uilayer.h b/uilayer.h index b49c0598..40e851a1 100644 --- a/uilayer.h +++ b/uilayer.h @@ -41,6 +41,8 @@ public: // methods: bool init( GLFWwindow *Window ); + void + set_unit( GLint const Textureunit ) { m_textureunit = Textureunit; } // draws requested UI elements void render(); @@ -83,6 +85,7 @@ private: // members: GLFWwindow *m_window { nullptr }; + GLint m_textureunit{ GL_TEXTURE0 }; GLuint m_fontbase { (GLuint)-1 }; // numer DL dla znaków w napisach // progress bar config. TODO: put these together into an object diff --git a/version.h b/version.h index a54feaa0..3a7fa547 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 17 -#define VERSION_MINOR 721 +#define VERSION_MINOR 724 #define VERSION_REVISION 0 From 2f1358422dfb1162ad176094fecfa77c8724891e Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Tue, 25 Jul 2017 17:42:43 +0200 Subject: [PATCH 5/6] multi texturing support for display list mode --- World.cpp | 2 -- openglgeometrybank.cpp | 30 +++++++++++++++++++++--------- openglgeometrybank.h | 27 +++++++++++++++++++-------- renderer.cpp | 4 ++-- uilayer.cpp | 2 +- 5 files changed, 43 insertions(+), 22 deletions(-) diff --git a/World.cpp b/World.cpp index f11c1e80..e6b5fea7 100644 --- a/World.cpp +++ b/World.cpp @@ -963,8 +963,6 @@ void TWorld::FollowView(bool wycisz) { + Train->GetDirection() * 5.0 * Train->Dynamic()->MoverParameters->ActiveCab; } Train->pMechOffset = Train->pMechSittingPosition; - - Global::SetCameraPosition( Train->Dynamic() ->GetPosition()); // tu ustawić nowÄ…, bo od niej liczÄ… siÄ™ odlegÅ‚oÅ›ci } } else diff --git a/openglgeometrybank.cpp b/openglgeometrybank.cpp index 1c2dec5d..0a046ead 100644 --- a/openglgeometrybank.cpp +++ b/openglgeometrybank.cpp @@ -98,9 +98,9 @@ geometry_bank::append( vertex_array &Vertices, geometry_handle const &Geometry ) // draws geometry stored in specified chunk void -geometry_bank::draw( geometry_handle const &Geometry, unsigned int const Streams ) { +geometry_bank::draw( geometry_handle const &Geometry, stream_units const &Units, unsigned int const Streams ) { // template method implementation - draw_( Geometry, Streams ); + draw_( Geometry, Units, Streams ); } // frees subclass-specific resources associated with the bank, typically called when the bank wasn't in use for a period of time @@ -148,7 +148,7 @@ opengl_vbogeometrybank::replace_( geometry_handle const &Geometry ) { // draw() subclass details void -opengl_vbogeometrybank::draw_( geometry_handle const &Geometry, unsigned int const Streams ) { +opengl_vbogeometrybank::draw_( geometry_handle const &Geometry, stream_units const &Units, unsigned int const Streams ) { if( m_buffer == NULL ) { // if there's no buffer, we'll have to make one @@ -203,7 +203,7 @@ opengl_vbogeometrybank::draw_( geometry_handle const &Geometry, unsigned int con chunkrecord.is_good = true; } if( m_activestreams != Streams ) { - bind_streams( Streams ); + bind_streams( Units, Streams ); } // ...render... ::glDrawArrays( chunk.type, chunkrecord.offset, chunkrecord.size ); @@ -239,7 +239,7 @@ opengl_vbogeometrybank::delete_buffer() { ::glDeleteBuffers( 1, &m_buffer ); if( m_activebuffer == m_buffer ) { m_activebuffer = NULL; - bind_streams( stream::none ); + release_streams(); } m_buffer = NULL; m_buffercapacity = 0; @@ -249,7 +249,7 @@ opengl_vbogeometrybank::delete_buffer() { } void -opengl_vbogeometrybank::bind_streams( unsigned int const Streams ) { +opengl_vbogeometrybank::bind_streams( stream_units const &Units, unsigned int const Streams ) { if( Streams & stream::position ) { ::glVertexPointer( 3, GL_FLOAT, sizeof( basic_vertex ), static_cast( nullptr ) ); @@ -274,6 +274,7 @@ opengl_vbogeometrybank::bind_streams( unsigned int const Streams ) { ::glDisableClientState( GL_COLOR_ARRAY ); } if( Streams & stream::texture ) { + ::glClientActiveTexture( Units.texture ); ::glTexCoordPointer( 2, GL_FLOAT, sizeof( basic_vertex ), static_cast( nullptr ) + 24 ); ::glEnableClientState( GL_TEXTURE_COORD_ARRAY ); } @@ -284,6 +285,17 @@ opengl_vbogeometrybank::bind_streams( unsigned int const Streams ) { m_activestreams = Streams; } +void +opengl_vbogeometrybank::release_streams() { + + ::glDisableClientState( GL_VERTEX_ARRAY ); + ::glDisableClientState( GL_NORMAL_ARRAY ); + ::glDisableClientState( GL_COLOR_ARRAY ); + ::glDisableClientState( GL_TEXTURE_COORD_ARRAY ); + + m_activestreams = stream::none; +} + // opengl display list based variant of the geometry bank // create() subclass details @@ -302,7 +314,7 @@ opengl_dlgeometrybank::replace_( geometry_handle const &Geometry ) { // draw() subclass details void -opengl_dlgeometrybank::draw_( geometry_handle const &Geometry, unsigned int const Streams ) { +opengl_dlgeometrybank::draw_( geometry_handle const &Geometry, stream_units const &Units, unsigned int const Streams ) { auto &chunkrecord = m_chunkrecords[ Geometry.chunk - 1 ]; if( chunkrecord.streams != Streams ) { @@ -319,7 +331,7 @@ opengl_dlgeometrybank::draw_( geometry_handle const &Geometry, unsigned int cons for( auto const &vertex : chunk.vertices ) { if( Streams & stream::normal ) { ::glNormal3fv( glm::value_ptr( vertex.normal ) ); } else if( Streams & stream::color ) { ::glColor3fv( glm::value_ptr( vertex.normal ) ); } - if( Streams & stream::texture ) { ::glTexCoord2fv( glm::value_ptr( vertex.texture ) ); } + if( Streams & stream::texture ) { ::glMultiTexCoord2fv( Units.texture, glm::value_ptr( vertex.texture ) ); } if( Streams & stream::position ) { ::glVertex3fv( glm::value_ptr( vertex.position ) ); } } ::glEnd(); @@ -404,7 +416,7 @@ geometrybank_manager::draw( geometry_handle const &Geometry, unsigned int const auto &bankrecord = bank( Geometry ); bankrecord.second = m_garbagecollector.timestamp(); - bankrecord.first->draw( Geometry, Streams ); + bankrecord.first->draw( Geometry, m_units, Streams ); } // provides direct access to vertex data of specfied chunk diff --git a/openglgeometrybank.h b/openglgeometrybank.h index ca4246a0..736f3d66 100644 --- a/openglgeometrybank.h +++ b/openglgeometrybank.h @@ -42,7 +42,12 @@ enum stream { }; unsigned int const basic_streams { stream::position | stream::normal | stream::texture }; -unsigned int const color_streams{ stream::position | stream::color | stream::texture }; +unsigned int const color_streams { stream::position | stream::color | stream::texture }; + +struct stream_units { + + GLint texture { GL_TEXTURE0 }; // unit associated with main texture data stream. TODO: allow multiple units per stream +}; typedef std::vector vertex_array; @@ -98,11 +103,11 @@ public: append( vertex_array &Vertices, geometry_handle const &Geometry ); // draws geometry stored in specified chunk void - draw( geometry_handle const &Geometry, unsigned int const Streams = basic_streams ); + draw( geometry_handle const &Geometry, stream_units const &Units, unsigned int const Streams = basic_streams ); // draws geometry stored in supplied list of chunks template void - draw( Iterator_ First, Iterator_ Last, unsigned int const Streams = basic_streams ) { while( First != Last ) { draw( *First, Streams ); ++First; } } + draw( Iterator_ First, Iterator_ Last, stream_units const &Units, unsigned int const Streams = basic_streams ) { while( First != Last ) { draw( *First, Units, Streams ); ++First; } } // frees subclass-specific resources associated with the bank, typically called when the bank wasn't in use for a period of time void release(); @@ -145,7 +150,7 @@ private: // replace() subclass details virtual void replace_( geometry_handle const &Geometry ) = 0; // draw() subclass details - virtual void draw_( geometry_handle const &Geometry, unsigned int const Streams ) = 0; + virtual void draw_( geometry_handle const &Geometry, stream_units const &Units, unsigned int const Streams ) = 0; // resource release subclass details virtual void release_() = 0; }; @@ -186,8 +191,8 @@ private: replace_( geometry_handle const &Geometry ); // draw() subclass details void - draw_( geometry_handle const &Geometry, unsigned int const Streams ); - // release () subclass details + draw_( geometry_handle const &Geometry, stream_units const &Units, unsigned int const Streams ); + // release() subclass details void release_(); void @@ -195,7 +200,9 @@ private: void delete_buffer(); void - bind_streams( unsigned int const Streams ); + bind_streams( stream_units const &Units, unsigned int const Streams ); + void + release_streams(); // members: static GLuint m_activebuffer; // buffer bound currently on the opengl end, if any @@ -236,7 +243,7 @@ private: replace_( geometry_handle const &Geometry ); // draw() subclass details void - draw_( geometry_handle const &Geometry, unsigned int const Streams ); + draw_( geometry_handle const &Geometry, stream_units const &Units, unsigned int const Streams ); // release () subclass details void release_(); @@ -282,6 +289,9 @@ public: // provides direct access to vertex data of specfied chunk vertex_array const & vertices( geometry_handle const &Geometry ) const; + // sets target texture unit for the texture data stream + stream_units & + units() { return m_units; } private: // types: @@ -294,6 +304,7 @@ private: // members: geometrybanktimepointpair_sequence m_geometrybanks; garbage_collector m_garbagecollector { m_geometrybanks, 60, 120, "geometry buffer" }; + stream_units m_units; // methods inline diff --git a/renderer.cpp b/renderer.cpp index c3ce5a93..3e59773f 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -71,7 +71,7 @@ opengl_renderer::Init( GLFWwindow *Window ) { glShadeModel( GL_SMOOTH ); // Enable Smooth Shading glActiveTexture( m_diffusetextureunit ); - glClientActiveTexture( m_diffusetextureunit ); + m_geometry.units().texture = m_diffusetextureunit; UILayer.set_unit( m_diffusetextureunit ); glEnable( GL_DEPTH_TEST ); @@ -566,7 +566,7 @@ void opengl_renderer::setup_camera_light_perspective( glm::dmat4 &Viewmatrix ) { m_renderpass.camera.position() = Global::pCameraPosition - glm::dvec3{ Global::DayLight.direction * m_renderpass.draw_range * 0.5f }; - m_renderpass.camera.position().y = std::max( 75.0, m_renderpass.camera.position().y ); // prevent shadow source from dipping too low + 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( m_renderpass.camera.position(), glm::dvec3{ Global::pCameraPosition.x, 0.0, Global::pCameraPosition.z }, diff --git a/uilayer.cpp b/uilayer.cpp index d3ee278e..d4a574ca 100644 --- a/uilayer.cpp +++ b/uilayer.cpp @@ -65,7 +65,7 @@ ui_layer::render() { glMatrixMode(GL_PROJECTION); glLoadIdentity(); - glOrtho( 0, Global::ScreenWidth, Global::ScreenHeight, 0, -1, 1 ); + glOrtho( 0, std::max( 1, Global::ScreenWidth ), std::max( 1, Global::ScreenHeight ), 0, -1, 1 ); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); From 000603877ca4e75ea677fa040127baa2bb467f9a Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Tue, 25 Jul 2017 18:34:24 +0200 Subject: [PATCH 6/6] build 170725. cab control sound support tweaks, excluded track nodes from casting shadows --- Gauge.cpp | 19 +++++++++++-------- renderer.cpp | 9 ++++++--- version.h | 2 +- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/Gauge.cpp b/Gauge.cpp index c2206bb7..4c4da7b2 100644 --- a/Gauge.cpp +++ b/Gauge.cpp @@ -139,8 +139,8 @@ TGauge::Load_mapping( cParser &Input ) { } else if( key.find( "sound" ) == 0 ) { // sounds assigned to specific gauge values, defined by key soundFoo: where Foo = value - auto const indexstart = key.find_first_of( "1234567890" ); - auto const indexend = key.find_first_not_of( "1234567890", indexstart ); + auto const indexstart = key.find_first_of( "-1234567890" ); + auto const indexend = key.find_first_not_of( "-1234567890", indexstart ); if( indexstart != std::string::npos ) { m_soundfxvalues.emplace( std::stoi( key.substr( indexstart, indexend - indexstart ) ), @@ -180,17 +180,20 @@ void TGauge::DecValue(double fNewDesired) void TGauge::UpdateValue( double fNewDesired, PSound Fallbacksound ) { - if( static_cast( std::round( 100.0 * ( fDesiredValue - fOffset ) / fScale ) ) == static_cast( 100.0 * fNewDesired ) ) { + auto const desiredtimes100 = static_cast( 100.0 * fNewDesired ); + if( static_cast( std::round( 100.0 * ( fDesiredValue - fOffset ) / fScale ) ) == desiredtimes100 ) { return; } fDesiredValue = fNewDesired * fScale + fOffset; // if there's any sound associated with new requested value, play it // check value-specific table first... - auto const desiredvalue = static_cast( std::round( fNewDesired ) ); - auto const lookup = m_soundfxvalues.find( desiredvalue ); - if( lookup != m_soundfxvalues.end() ) { - play( lookup->second ); - return; + if( desiredtimes100 % 100 == 0 ) { + // filter out values other than full integers + auto const lookup = m_soundfxvalues.find( desiredtimes100 / 100 ); + if( lookup != m_soundfxvalues.end() ) { + play( lookup->second ); + return; + } } // ...and if there isn't any, fall back on the basic set... auto const currentvalue = GetValue(); diff --git a/renderer.cpp b/renderer.cpp index 3e59773f..a1243a02 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -1258,10 +1258,10 @@ opengl_renderer::Render( TGroundNode *Node ) { case TP_TRACK: { // setup - ::glPushMatrix(); - auto const originoffset = Node->m_rootposition - m_renderpass.camera.position(); - ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); switch( m_renderpass.draw_mode ) { + case rendermode::shadows: { + return false; + } case rendermode::pickscenery: { // add the node to the pick list m_picksceneryitems.emplace_back( Node ); @@ -1271,6 +1271,9 @@ opengl_renderer::Render( TGroundNode *Node ) { break; } } + ::glPushMatrix(); + auto const originoffset = Node->m_rootposition - m_renderpass.camera.position(); + ::glTranslated( originoffset.x, originoffset.y, originoffset.z ); // render Render( Node->pTrack ); // post-render cleanup diff --git a/version.h b/version.h index 3a7fa547..122d37e8 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 17 -#define VERSION_MINOR 724 +#define VERSION_MINOR 725 #define VERSION_REVISION 0