From 9cacf191c60046853da1947eead273e9a9db326d Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Tue, 9 Oct 2018 18:48:24 +0200 Subject: [PATCH] submodel opacity parameter, additional light blinking modes, scenario event queue filtering --- AnimModel.cpp | 110 +++++++++++++++++++++++++++++++++++++-------- AnimModel.h | 11 +++-- Driver.cpp | 4 +- Model3d.cpp | 23 +++++++++- Model3d.h | 6 ++- Traction.cpp | 2 +- driveruipanels.cpp | 68 +++++++++++++++------------- driveruipanels.h | 3 +- renderer.cpp | 36 +++++++++++---- 9 files changed, 195 insertions(+), 68 deletions(-) diff --git a/AnimModel.cpp b/AnimModel.cpp index be263b5b..c004a647 100644 --- a/AnimModel.cpp +++ b/AnimModel.cpp @@ -444,8 +444,9 @@ bool TAnimModel::Init(std::string const &asName, std::string const &asReplacable // tekstura nieprzezroczysta - nie renderować w cyklu przezroczystych m_materialdata.textures_alpha = 0x30300030; } - - fBlinkTimer = double( Random( 1000 * fOffTime ) ) / ( 1000 * fOffTime ); + +// TODO: redo the random timer initialization +// fBlinkTimer = Random() * ( fOnTime + fOffTime ); pModel = TModelsManager::GetModel( asName ); return ( pModel != nullptr ); @@ -547,9 +548,73 @@ void TAnimModel::RaAnimate( unsigned int const Framestamp ) { if( Framestamp == m_framestamp ) { return; } - fBlinkTimer -= Timer::GetDeltaTime(); - if( fBlinkTimer <= 0 ) - fBlinkTimer += fOffTime; + auto const timedelta { Timer::GetDeltaTime() }; + + // interpretacja ułamka zależnie od typu + // case ls_Off: ustalenie czasu migotania, t<1s (f>1Hz), np. 0.1 => t=0.1 (f=10Hz) + // case ls_On: ustalenie wypełnienia ułamkiem, np. 1.25 => zapalony przez 1/4 okresu + // case ls_Blink: ustalenie częstotliwości migotania, f<1Hz (t>1s), np. 2.2 => f=0.2Hz (t=5s) + float modeintegral, modefractional; + for( int idx = 0; idx < iNumLights; ++idx ) { + + modefractional = std::modf( std::abs( lsLights[ idx ] ), &modeintegral ); + + if( modeintegral >= ls_Dark ) { + // light threshold modes don't use timers + continue; + } + auto const mode { static_cast( modeintegral ) }; + + auto &opacity { m_lightopacities[ idx ] }; + auto &timer { m_lighttimers[ idx ] }; + if( ( modeintegral < ls_Blink ) && ( modefractional < 0.01f ) ) { + // simple flip modes + switch( mode ) { + case ls_Off: { + // reduce to zero + timer = std::max( 0.f, timer - timedelta ); + break; + } + case ls_On: { + // increase to max value + timer = std::min( fTransitionTime, timer + timedelta ); + break; + } + default: { + break; + } + } + opacity = timer / fTransitionTime; + } + else { + // blink modes + auto const ontime { ( + ( mode == ls_Blink ) ? ( ( modefractional < 0.01f ) ? fOnTime : ( 1.f / modefractional ) * 0.5f ) : + ( mode == ls_Off ) ? modefractional * 0.5f : + ( mode == ls_On ) ? modefractional * ( fOnTime + fOffTime ) : + fOnTime ) }; // fallback + auto const offtime { ( + ( mode == ls_Blink ) ? ( ( modefractional < 0.01f ) ? fOffTime : ontime ) : + ( mode == ls_Off ) ? ontime : + ( mode == ls_On ) ? ( fOnTime + fOffTime ) - ontime : + fOffTime ) }; // fallback + auto const transitiontime { + std::min( + 1.f, + std::min( ontime, offtime ) * 0.9f ) }; + + timer = clamp_circular( timer + timedelta * ( lsLights[ idx ] > 0.f ? 1.f : -1.f ), ontime + offtime ); + // set opacity depending on blink stage + if( timer < ontime ) { + // blink on + opacity = clamp( timer / transitiontime, 0.f, 1.f ); + } + else { + // blink off + opacity = 1.f - clamp( ( timer - ontime ) / transitiontime, 0.f, 1.f ); + } + } + } // Ra 2F1I: to by można pomijać dla modeli bez animacji, których jest większość TAnimContainer *pCurrent; @@ -568,17 +633,19 @@ void TAnimModel::RaPrepare() bool state; // stan światła for (int i = 0; i < iNumLights; ++i) { - auto const lightmode { static_cast( lsLights[ i ] ) }; + auto const lightmode { static_cast( std::abs( lsLights[ i ] ) ) }; switch( lightmode ) { case ls_On: - case ls_Off: { - // zapalony albo zgaszony - state = ( lightmode == ls_On ); - break; - } + case ls_Off: case ls_Blink: { - // migotanie - state = ( fBlinkTimer < fOnTime ); + if (LightsOn[i]) { + LightsOn[i]->iVisible = ( m_lightopacities[i] > 0.f ); + LightsOn[i]->SetVisibilityLevel( m_lightopacities[i], true, false ); + } + if (LightsOff[i]) { + LightsOff[i]->iVisible = ( m_lightopacities[i] < 1.f ); + LightsOff[i]->SetVisibilityLevel( 1.f, true, false ); + } break; } case ls_Dark: { @@ -606,10 +673,16 @@ void TAnimModel::RaPrepare() break; } } - if (LightsOn[i]) - LightsOn[i]->iVisible = state; - if (LightsOff[i]) - LightsOff[i]->iVisible = !state; + if( lightmode >= ls_Dark ) { + // crude as hell but for test will do :x + if (LightsOn[i]) { + LightsOn[i]->iVisible = state; + // TODO: set visibility for the entire submodel's children as well + LightsOn[i]->fVisible = m_lightopacities[i]; + } + if (LightsOff[i]) + LightsOff[i]->iVisible = !state; + } } TSubModel::iInstance = reinterpret_cast( this ); //żeby nie robić cudzych animacji TSubModel::pasText = &asText; // przekazanie tekstu do wyświetlacza (!!!! do przemyślenia) @@ -774,8 +847,9 @@ void TAnimModel::AnimationVND(void *pData, double a, double b, double c, double //--------------------------------------------------------------------------- void TAnimModel::LightSet(int const n, float const v) { // ustawienie światła (n) na wartość (v) - if (n >= iMaxNumLights) + if( n >= iMaxNumLights ) { return; // przekroczony zakres + } lsLights[ n ] = v; }; diff --git a/AnimModel.h b/AnimModel.h index 6384f164..63f06a59 100644 --- a/AnimModel.h +++ b/AnimModel.h @@ -179,7 +179,7 @@ private: // members TAnimContainer *pRoot { nullptr }; // pojemniki sterujące, tylko dla aniomowanych submodeli TModel3d *pModel { nullptr }; - double fBlinkTimer { 0.0 }; +// double fBlinkTimer { 0.0 }; int iNumLights { 0 }; TSubModel *LightsOn[ iMaxNumLights ]; // Ra: te wskaźniki powinny być w ramach TModel3d TSubModel *LightsOff[ iMaxNumLights ]; @@ -188,10 +188,13 @@ private: std::string asText; // tekst dla wyświetlacza znakowego TAnimAdvanced *pAdvanced { nullptr }; + // TODO: wrap into a light state struct float lsLights[ iMaxNumLights ]; -// float fDark { DefaultDarkThresholdLevel }; // poziom zapalanie światła (powinno być chyba powiązane z danym światłem?) - float fOnTime { 0.66f }; - float fOffTime { 0.66f + 0.66f }; // były stałymi, teraz mogą być zmienne dla każdego egzemplarza + std::array m_lighttimers { 0.f }; + std::array m_lightopacities { 1.f }; + float fOnTime { 1.f / 2 };// { 60.f / 45.f / 2 }; + float fOffTime { 1.f / 2 };// { 60.f / 45.f / 2 }; // były stałymi, teraz mogą być zmienne dla każdego egzemplarza + float fTransitionTime { fOnTime * 0.9f }; // time unsigned int m_framestamp { 0 }; // id of last rendered gfx frame }; diff --git a/Driver.cpp b/Driver.cpp index ddba2e45..1a7daf0a 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -342,8 +342,8 @@ std::string TSpeedPos::TableText() const { // pozycja tabelki pr?dko?ci if (iFlags & spEnabled) { // o ile pozycja istotna - return "Flags:" + to_hex_str(iFlags, 8) + ", Dist:" + to_string(fDist, 1, 6) + - ", Vel:" + (fVelNext == -1.0 ? " - " : to_string(static_cast(fVelNext), 0, 3)) + ", Name:" + GetName(); + return to_hex_str(iFlags, 8) + " " + to_string(fDist, 1, 6) + + " " + (fVelNext == -1.0 ? " -" : to_string(static_cast(fVelNext), 0, 3)) + " " + GetName(); } return "Empty"; } diff --git a/Model3d.cpp b/Model3d.cpp index 272cb7cb..ecf7ed53 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -71,6 +71,23 @@ void TSubModel::Name(std::string const &Name) pName = Name; }; +// sets visibility level (alpha component) to specified value +void +TSubModel::SetVisibilityLevel( float const Level, bool const Includechildren, bool const Includesiblings ) { + + fVisible = Level; + if( true == Includesiblings ) { + auto sibling { this }; + while( ( sibling = sibling->Next ) != nullptr ) { + sibling->SetVisibilityLevel( Level, Includechildren, false ); // no need for all siblings to duplicate the work + } + } + if( ( true == Includechildren ) + && ( Child != nullptr ) ) { + Child->SetVisibilityLevel( Level, Includechildren, true ); // node's children include child's siblings and children + } +} + // sets light level (alpha component of illumination color) to specified value void TSubModel::SetLightLevel( float const Level, bool const Includechildren, bool const Includesiblings ) { @@ -1313,7 +1330,8 @@ void TSubModel::serialize(std::ostream &s, else sn_utils::ls_int32(s, (int32_t)get_container_pos(textures, m_materialname)); - sn_utils::ls_float32(s, fVisible); +// sn_utils::ls_float32(s, fVisible); + sn_utils::ls_float32(s, 1.f); sn_utils::ls_float32(s, fLight); sn_utils::s_vec4(s, f4Ambient); @@ -1433,7 +1451,8 @@ void TSubModel::deserialize(std::istream &s) tVboPtr = sn_utils::ld_int32(s); iTexture = sn_utils::ld_int32(s); - fVisible = sn_utils::ld_float32(s); +// fVisible = sn_utils::ld_float32(s); + auto discard = sn_utils::ld_float32(s); fLight = sn_utils::ld_float32(s); f4Ambient = sn_utils::d_vec4(s); diff --git a/Model3d.h b/Model3d.h index 7c8317a1..68a82983 100644 --- a/Model3d.h +++ b/Model3d.h @@ -98,7 +98,6 @@ private: int iNumVerts { -1 }; // ilość wierzchołków (1 dla FreeSpotLight) int tVboPtr; // początek na liście wierzchołków albo indeksów int iTexture { 0 }; // numer nazwy tekstury, -1 wymienna, 0 brak - float fVisible { 0.0f }; // próg jasności światła do załączenia submodelu float fLight { -1.0f }; // próg jasności światła do zadziałania selfillum glm::vec4 f4Ambient { 1.0f,1.0f,1.0f,1.0f }, @@ -140,7 +139,8 @@ public: // chwilowo float4x4 *mAnimMatrix{ nullptr }; // macierz do animacji kwaternionowych (należy do AnimContainer) TSubModel **smLetter{ nullptr }; // wskaźnik na tablicę submdeli do generoania tekstu (docelowo zapisać do E3D) TSubModel *Parent{ nullptr }; // nadrzędny, np. do wymnażania macierzy - int iVisible{ 1 }; // roboczy stan widoczności + int iVisible { 1 }; // roboczy stan widoczności + float fVisible { 1.f }; // visibility level std::string m_materialname; // robocza nazwa tekstury do zapisania w pliku binarnym std::string pName; // robocza nazwa private: @@ -192,6 +192,8 @@ public: int Flags() const { return iFlags; }; void UnFlagNext() { iFlags &= 0x00FFFFFF; }; void ColorsSet( glm::vec3 const &Ambient, glm::vec3 const &Diffuse, glm::vec3 const &Specular ); + // sets visibility level (alpha component) to specified value + void SetVisibilityLevel( float const Level, bool const Includechildren = false, bool const Includesiblings = false ); // sets light level (alpha component of illumination color) to specified value void SetLightLevel( float const Level, bool const Includechildren = false, bool const Includesiblings = false ); inline float3 Translation1Get() { diff --git a/Traction.cpp b/Traction.cpp index 28ff95a7..9839ea64 100644 --- a/Traction.cpp +++ b/Traction.cpp @@ -565,7 +565,7 @@ TTraction::wire_color() const { color.r *= Global.DayLight.ambient[ 0 ]; color.g *= Global.DayLight.ambient[ 1 ]; color.b *= Global.DayLight.ambient[ 2 ]; - color *= 0.5f; + color *= 0.35f; } else { // tymczasowo pokazanie zasilanych odcinków diff --git a/driveruipanels.cpp b/driveruipanels.cpp index ceb9253f..71572dc1 100644 --- a/driveruipanels.cpp +++ b/driveruipanels.cpp @@ -356,7 +356,10 @@ debug_panel::render() { render_section( "Vehicle AI", m_ailines ); render_section( "Vehicle Scan Table", m_scantablelines ); render_section( "Scenario", m_scenariolines ); - render_section( "Scenario Event Queue", m_eventqueuelines ); + if( true == render_section( "Scenario Event Queue", m_eventqueuelines ) ) { + // event queue filter + ImGui::Checkbox( "By This Vehicle Only", &m_eventqueueactivevehicleonly ); + } render_section( "Camera", m_cameralines ); render_section( "Gfx Renderer", m_rendererlines ); // toggles @@ -764,6 +767,8 @@ debug_panel::update_section_scantable( std::vector &Output ) { if( m_input.mechanik == nullptr ) { return; } + Output.emplace_back( "Flags: Dist: Vel: Name:", Global.UITextColor ); + auto const &mechanik{ *m_input.mechanik }; std::size_t i = 0; std::size_t const speedtablesize = clamp( static_cast( mechanik.TableSize() ) - 1, 0, 30 ); @@ -773,8 +778,8 @@ debug_panel::update_section_scantable( std::vector &Output ) { Output.emplace_back( Bezogonkow( scanline ), Global.UITextColor ); ++i; } while( i < speedtablesize ); - if( Output.empty() ) { - Output.emplace_back( "(no points of interest)", Global.UITextColor ); + if( Output.size() == 1 ) { + Output.front().data = "(no points of interest)"; } } @@ -787,7 +792,8 @@ debug_panel::update_section_scenario( std::vector &Output ) { Output.emplace_back( textline, Global.UITextColor ); // current luminance level - textline = "Light level: " + to_string( Global.fLuminance, 3 ); + textline = "Cloud cover: " + to_string( Global.Overcast, 3 ); + textline += "\nLight level: " + to_string( Global.fLuminance, 3 ); if( Global.FakeLight ) { textline += "(*)"; } textline += "\nAir temperature: " + to_string( Global.AirTemperature, 1 ) + " deg C"; @@ -799,32 +805,33 @@ debug_panel::update_section_eventqueue( std::vector &Output ) { std::string textline; - // current event queue - auto const time { Timer::GetTime() }; - auto const *event { simulation::Events.begin() }; - auto eventtableindex{ 0 }; - while( ( event != nullptr ) - && ( eventtableindex < 30 ) ) { + // current event queue + auto const time { Timer::GetTime() }; + auto const *event { simulation::Events.begin() }; - if( ( false == event->m_ignored ) - && ( false == event->m_passive ) ) { + Output.emplace_back( "Delay: Event:", Global.UITextColor ); - auto const delay { " " + to_string( std::max( 0.0, event->m_launchtime - time ), 1 ) }; - textline = - "Delay: " + delay.substr( delay.length() - 6 ) - + ", Event: " + event->m_name - + ( event->m_activator ? " (by: " + event->m_activator->asName + ")" : "" ) - + ( event->m_sibling ? " (joint event)" : "" ); + while( ( event != nullptr ) + && ( Output.size() < 30 ) ) { - Output.emplace_back( textline, Global.UITextColor ); - ++eventtableindex; - } - event = event->m_next; - } - if( Output.empty() ) { - textline = "(no queued events)"; - Output.emplace_back( textline, Global.UITextColor ); - } + if( ( false == event->m_ignored ) + && ( false == event->m_passive ) + && ( ( false == m_eventqueueactivevehicleonly ) + || ( event->m_activator == m_input.vehicle ) ) ) { + + auto const delay { " " + to_string( std::max( 0.0, event->m_launchtime - time ), 1 ) }; + textline = delay.substr( delay.length() - 6 ) + + " " + event->m_name + + ( event->m_activator ? " (by: " + event->m_activator->asName + ")" : "" ) + + ( event->m_sibling ? " (joint event)" : "" ); + + Output.emplace_back( textline, Global.UITextColor ); + } + event = event->m_next; + } + if( Output.size() == 1 ) { + Output.front().data = "(no queued events)"; + } } void @@ -888,15 +895,16 @@ debug_panel::update_section_renderer( std::vector &Output ) { Output.emplace_back( GfxRenderer.info_stats(), Global.UITextColor ); } -void +bool debug_panel::render_section( std::string const &Header, std::vector const &Lines ) { - if( Lines.empty() ) { return; } - if( false == ImGui::CollapsingHeader( Header.c_str() ) ) { return; } + if( true == Lines.empty() ) { return false; } + if( false == ImGui::CollapsingHeader( Header.c_str() ) ) { return false; } for( auto const &line : Lines ) { ImGui::TextColored( ImVec4( line.color.r, line.color.g, line.color.b, line.color.a ), line.data.c_str() ); } + return true; } void diff --git a/driveruipanels.h b/driveruipanels.h index abd09a3f..505565be 100644 --- a/driveruipanels.h +++ b/driveruipanels.h @@ -76,7 +76,7 @@ private: std::string update_vehicle_coupler( int const Side ); std::string update_vehicle_brake() const; // renders provided lines, under specified collapsing header - void render_section( std::string const &Header, std::vector const &Lines ); + bool render_section( std::string const &Header, std::vector const &Lines ); // members std::array m_buffer; input_data m_input; @@ -92,6 +92,7 @@ private: int tprev { 0 }; // poprzedni czas double VelPrev { 0.0 }; // poprzednia prędkość double Acc { 0.0 }; // przyspieszenie styczne + bool m_eventqueueactivevehicleonly { false }; }; class transcripts_panel : public ui_panel { diff --git a/renderer.cpp b/renderer.cpp index a18be646..f172bf0b 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -2409,7 +2409,20 @@ opengl_renderer::Render( TSubModel *Submodel ) { Bind_Material( Submodel->m_material ); } // ...colors... - ::glColor3fv( glm::value_ptr( Submodel->f4Diffuse ) ); // McZapkie-240702: zamiast ub + if( Submodel->fVisible < 1.f ) { + // setup + ::glAlphaFunc( GL_GREATER, 0.f ); + ::glEnable( GL_BLEND ); + ::glColor4f( + Submodel->f4Diffuse.r, + Submodel->f4Diffuse.g, + Submodel->f4Diffuse.b, + Submodel->fVisible ); + } + else { + ::glColor3fv( glm::value_ptr( Submodel->f4Diffuse ) ); // McZapkie-240702: zamiast ub + } + // ...specular... if( ( true == m_renderspecular ) && ( m_sunlight.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 * m_sunlight.specular.a * m_specularopaquescalefactor ) ); @@ -2441,6 +2454,10 @@ opengl_renderer::Render( TSubModel *Submodel ) { } */ // post-draw reset + if( Submodel->fVisible < 1.f ) { + ::glAlphaFunc( GL_GREATER, 0.5f ); + ::glDisable( GL_BLEND ); + } if( ( true == m_renderspecular ) && ( m_sunlight.specular.a > 0.01f ) ) { ::glMaterialfv( GL_FRONT, GL_SPECULAR, glm::value_ptr( colors::none ) ); } @@ -2535,7 +2552,7 @@ opengl_renderer::Render( TSubModel *Submodel ) { // 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 { std::max( 0.5f, ( Submodel->fSquareMaxDist - TSubModel::fSquareDist ) / Submodel->fSquareMaxDist ) }; - float const precipitationfactor { std::max( 1.f, Global.Overcast - 1.f ) }; + float const precipitationfactor { std::max( 1.f, 0.5f * ( Global.Overcast - 1.f ) ) }; if( lightlevel > 0.f ) { // material configuration: @@ -2543,9 +2560,11 @@ opengl_renderer::Render( TSubModel *Submodel ) { Bind_Material( null_handle ); ::glPointSize( std::max( 3.f, 5.f * distancefactor * anglefactor ) ); - ::glColor4f( Submodel->f4Diffuse[ 0 ], Submodel->f4Diffuse[ 1 ], Submodel->f4Diffuse[ 2 ], std::min( 1.f, lightlevel * anglefactor * precipitationfactor ) ); + ::glColor4f( Submodel->f4Diffuse[ 0 ], Submodel->f4Diffuse[ 1 ], Submodel->f4Diffuse[ 2 ], Submodel->fVisible * std::min( 1.f, lightlevel * anglefactor * precipitationfactor ) ); ::glDisable( GL_LIGHTING ); + ::glDisable( GL_FOG ); ::glEnable( GL_BLEND ); + ::glAlphaFunc( GL_GREATER, 0.f ); ::glPushMatrix(); ::glLoadIdentity(); @@ -3439,10 +3458,10 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { float glarelevel = 0.6f; // luminosity at night is at level of ~0.1, so the overall resulting transparency in clear conditions is ~0.5 at full 'brightness' if( Submodel->fCosViewAngle > Submodel->fCosFalloffAngle ) { // only bother if the viewer is inside the visibility cone - auto glarelevel { clamp( - 0.6f - - Global.fLuminance // reduce the glare in bright daylight - + std::max( 0.f, Global.Overcast - 1.f ), // increase the glare in rainy/foggy conditions + auto glarelevel { clamp( + std::max( + 0.6f - Global.fLuminance, // reduce the glare in bright daylight + Global.Overcast - 1.f ), // ensure some glare in rainy/foggy conditions 0.f, 1.f ) }; // scale it down based on view angle glarelevel *= ( Submodel->fCosViewAngle - Submodel->fCosFalloffAngle ) / ( 1.0f - Submodel->fCosFalloffAngle ); @@ -3452,8 +3471,9 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { ::glPushAttrib( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_ENABLE_BIT ); Bind_Texture( m_glaretexture ); - ::glColor4f( Submodel->f4Diffuse[ 0 ], Submodel->f4Diffuse[ 1 ], Submodel->f4Diffuse[ 2 ], glarelevel ); + ::glColor4f( Submodel->f4Diffuse[ 0 ], Submodel->f4Diffuse[ 1 ], Submodel->f4Diffuse[ 2 ], Submodel->fVisible * glarelevel ); ::glDisable( GL_LIGHTING ); + ::glDisable( GL_FOG ); ::glDepthMask( GL_FALSE ); ::glBlendFunc( GL_SRC_ALPHA, GL_ONE );