#include "drape_frontend/traffic_renderer.hpp" #include "drape_frontend/color_constants.hpp" #include "drape_frontend/visual_params.hpp" #include "drape/glsl_func.hpp" #include "drape/shader_def.hpp" #include "drape/support_manager.hpp" #include "drape/vertex_array_buffer.hpp" #include "indexer/map_style_reader.hpp" #include "indexer/scales.hpp" #include "base/logging.hpp" #include "std/algorithm.hpp" namespace df { namespace { int constexpr kMinVisibleArrowZoomLevel = 16; int constexpr kRoadClass2MinVisibleArrowZoomLevel = 17; int constexpr kOutlineMinZoomLevel = 14; float const kTrafficArrowAspect = 24.0f / 8.0f; float const kLeftWidthInPixel[] = { // 1 2 3 4 5 6 7 8 9 10 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, //11 12 13 14 15 16 17 18 19 20 0.5f, 0.5f, 0.5f, 0.5f, 2.0f, 2.5f, 3.0f, 4.0f, 4.0f, 4.0f }; float const kRightWidthInPixel[] = { // 1 2 3 4 5 6 7 8 9 10 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 3.0f, 3.0f, //11 12 13 14 15 16 17 18 19 20 3.0f, 3.0f, 4.0f, 4.0f, 2.0f, 2.5f, 3.0f, 4.0f, 4.0f, 4.0f }; float const kRoadClass1WidthScalar[] = { // 1 2 3 4 5 6 7 8 9 10 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.3, //11 12 13 14 15 16 17 18 19 20 0.3, 0.3f, 0.4f, 0.5f, 0.6f, 0.6f, 1.0f, 1.0f, 1.0f, 1.0f }; float const kRoadClass2WidthScalar[] = { // 1 2 3 4 5 6 7 8 9 10 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.3f, //11 12 13 14 15 16 17 18 19 20 0.3f, 0.3f, 0.3f, 0.3f, 0.5f, 0.5f, 0.5f, 0.8f, 0.9f, 1.0f }; vector const kLineDrawerRoadClass1 = {12, 13, 14}; vector const kLineDrawerRoadClass2 = {15, 16}; float CalculateHalfWidth(ScreenBase const & screen, RoadClass const & roadClass, bool left) { double const zoomLevel = GetZoomLevel(screen.GetScale()); double zoom = trunc(zoomLevel); int const index = zoom - 1.0; float const lerpCoef = zoomLevel - zoom; float const * widthScalar = nullptr; if (roadClass == RoadClass::Class1) widthScalar = kRoadClass1WidthScalar; else if (roadClass == RoadClass::Class2) widthScalar = kRoadClass2WidthScalar; float const * halfWidth = left ? kLeftWidthInPixel : kRightWidthInPixel; float radius = 0.0f; if (index < scales::UPPER_STYLE_SCALE) { radius = halfWidth[index] + lerpCoef * (halfWidth[index + 1] - halfWidth[index]); if (widthScalar != nullptr) radius *= (widthScalar[index] + lerpCoef * (widthScalar[index + 1] - widthScalar[index])); } else { radius = halfWidth[scales::UPPER_STYLE_SCALE]; if (widthScalar != nullptr) radius *= widthScalar[scales::UPPER_STYLE_SCALE]; } return radius * VisualParams::Instance().GetVisualScale(); } } // namespace void TrafficRenderer::AddRenderData(ref_ptr mng, TrafficRenderData && renderData) { // Remove obsolete render data. TileKey const tileKey(renderData.m_tileKey); m_renderData.erase(remove_if(m_renderData.begin(), m_renderData.end(), [&tileKey](TrafficRenderData const & rd) { return tileKey == rd.m_tileKey && rd.m_tileKey.m_generation < tileKey.m_generation; }), m_renderData.end()); // Add new render data. m_renderData.emplace_back(move(renderData)); TrafficRenderData & rd = m_renderData.back(); ref_ptr program = mng->GetProgram(rd.m_state.GetProgramIndex()); program->Bind(); rd.m_bucket->GetBuffer()->Build(program); } void TrafficRenderer::OnUpdateViewport(CoverageResult const & coverage, int currentZoomLevel, buffer_vector const & tilesToDelete) { m_renderData.erase(remove_if(m_renderData.begin(), m_renderData.end(), [&coverage, ¤tZoomLevel, &tilesToDelete](TrafficRenderData const & rd) { return rd.m_tileKey.m_zoomLevel == currentZoomLevel && (rd.m_tileKey.m_x < coverage.m_minTileX || rd.m_tileKey.m_x >= coverage.m_maxTileX || rd.m_tileKey.m_y < coverage.m_minTileY || rd.m_tileKey.m_y >= coverage.m_maxTileY || find(tilesToDelete.begin(), tilesToDelete.end(), rd.m_tileKey) != tilesToDelete.end()); }), m_renderData.end()); } void TrafficRenderer::OnGeometryReady(int currentZoomLevel) { m_renderData.erase(remove_if(m_renderData.begin(), m_renderData.end(), [¤tZoomLevel](TrafficRenderData const & rd) { return rd.m_tileKey.m_zoomLevel != currentZoomLevel; }), m_renderData.end()); } void TrafficRenderer::RenderTraffic(ScreenBase const & screen, int zoomLevel, float opacity, ref_ptr mng, dp::UniformValuesStorage const & commonUniforms) { if (m_renderData.empty() || zoomLevel < kRoadClass0ZoomLevel) return; auto const style = GetStyleReader().GetCurrentStyle(); dp::Color const lightArrowColor = df::GetColorConstant(style, df::TrafficArrowLight); dp::Color const darkArrowColor = df::GetColorConstant(style, df::TrafficArrowDark); dp::Color const outlineColor = df::GetColorConstant(style, df::TrafficOutline); GLFunctions::glClearDepth(); for (TrafficRenderData & renderData : m_renderData) { if (renderData.m_state.GetDrawAsLine()) { ref_ptr program = mng->GetProgram(renderData.m_state.GetProgramIndex()); program->Bind(); dp::ApplyState(renderData.m_state, program); dp::UniformValuesStorage uniforms = commonUniforms; math::Matrix const mv = renderData.m_tileKey.GetTileBasedModelView(screen); uniforms.SetMatrix4x4Value("modelView", mv.m_data); uniforms.SetFloatValue("u_opacity", opacity); dp::ApplyUniforms(uniforms, program); renderData.m_bucket->Render(true /* draw as line */); } else { // Filter by road class. float leftPixelHalfWidth = 0.0f; float invLeftPixelLength = 0.0f; float rightPixelHalfWidth = 0.0f; int minVisibleArrowZoomLevel = kMinVisibleArrowZoomLevel; float outline = 0.0f; int visibleZoomLevel = kRoadClass0ZoomLevel; if (renderData.m_roadClass == RoadClass::Class0) { outline = (zoomLevel <= kOutlineMinZoomLevel ? 1.0 : 0.0); } else if (renderData.m_roadClass == RoadClass::Class1) { outline = (zoomLevel <= kOutlineMinZoomLevel ? 1.0 : 0.0); visibleZoomLevel = kRoadClass1ZoomLevel; } else if (renderData.m_roadClass == RoadClass::Class2) { visibleZoomLevel = kRoadClass2ZoomLevel; minVisibleArrowZoomLevel = kRoadClass2MinVisibleArrowZoomLevel; } if (zoomLevel < visibleZoomLevel) continue; leftPixelHalfWidth = CalculateHalfWidth(screen, renderData.m_roadClass, true /* left */); invLeftPixelLength = 1.0f / (2.0f * leftPixelHalfWidth * kTrafficArrowAspect); rightPixelHalfWidth = CalculateHalfWidth(screen, renderData.m_roadClass, false /* left */); float const kEps = 1e-5; if (fabs(leftPixelHalfWidth) < kEps && fabs(rightPixelHalfWidth) < kEps) continue; ref_ptr program = mng->GetProgram(renderData.m_state.GetProgramIndex()); program->Bind(); dp::ApplyState(renderData.m_state, program); dp::UniformValuesStorage uniforms = commonUniforms; math::Matrix const mv = renderData.m_tileKey.GetTileBasedModelView(screen); uniforms.SetMatrix4x4Value("modelView", mv.m_data); uniforms.SetFloatValue("u_opacity", opacity); uniforms.SetFloatValue("u_outline", outline); uniforms.SetFloatValue("u_lightArrowColor", lightArrowColor.GetRedF(), lightArrowColor.GetGreenF(), lightArrowColor.GetBlueF()); uniforms.SetFloatValue("u_darkArrowColor", darkArrowColor.GetRedF(), darkArrowColor.GetGreenF(), darkArrowColor.GetBlueF()); uniforms.SetFloatValue("u_outlineColor", outlineColor.GetRedF(), outlineColor.GetGreenF(), outlineColor.GetBlueF()); uniforms.SetFloatValue("u_trafficParams", leftPixelHalfWidth, rightPixelHalfWidth, invLeftPixelLength, zoomLevel >= minVisibleArrowZoomLevel ? 1.0f : 0.0f); dp::ApplyUniforms(uniforms, program); renderData.m_bucket->Render(false /* draw as line */); } } } void TrafficRenderer::ClearGLDependentResources() { m_renderData.clear(); } void TrafficRenderer::Clear(MwmSet::MwmId const & mwmId) { auto removePredicate = [&mwmId](TrafficRenderData const & data) { return data.m_mwmId == mwmId; }; m_renderData.erase(remove_if(m_renderData.begin(), m_renderData.end(), removePredicate), m_renderData.end()); } // static float TrafficRenderer::GetPixelWidth(RoadClass const & roadClass, int zoomLevel) { int width = 0; if (CanBeRendereredAsLine(roadClass, zoomLevel, width)) return static_cast(width); return GetPixelWidthInternal(roadClass, zoomLevel); } // static float TrafficRenderer::GetPixelWidthInternal(RoadClass const & roadClass, int zoomLevel) { ASSERT_GREATER(zoomLevel, 1, ()); ASSERT_LESS_OR_EQUAL(zoomLevel, scales::GetUpperStyleScale(), ()); float const * widthScalar = nullptr; if (roadClass == RoadClass::Class1) widthScalar = kRoadClass1WidthScalar; else if (roadClass == RoadClass::Class2) widthScalar = kRoadClass2WidthScalar; int const index = zoomLevel - 1; float const baseWidth = (kLeftWidthInPixel[index] + kRightWidthInPixel[index]) * df::VisualParams::Instance().GetVisualScale(); return (widthScalar != nullptr) ? (baseWidth * widthScalar[index]) : baseWidth; } // static bool TrafficRenderer::CanBeRendereredAsLine(RoadClass const & roadClass, int zoomLevel, int & width) { if (roadClass == RoadClass::Class0) return false; vector const * lineDrawer = nullptr; if (roadClass == RoadClass::Class1) lineDrawer = &kLineDrawerRoadClass1; else if (roadClass == RoadClass::Class2) lineDrawer = &kLineDrawerRoadClass2; ASSERT(lineDrawer != nullptr, ()); auto it = find(lineDrawer->begin(), lineDrawer->end(), zoomLevel); if (it == lineDrawer->end()) return false; width = max(1, my::rounds(TrafficRenderer::GetPixelWidthInternal(roadClass, zoomLevel))); return width <= dp::SupportManager::Instance().GetMaxLineWidth(); } } // namespace df