diff --git a/drape_frontend/path_text_shape.cpp b/drape_frontend/path_text_shape.cpp index 9f1939bf91..7ecdd326a9 100644 --- a/drape_frontend/path_text_shape.cpp +++ b/drape_frontend/path_text_shape.cpp @@ -1,7 +1,6 @@ #include "drape_frontend/path_text_shape.hpp" #include "drape_frontend/text_handle.hpp" #include "drape_frontend/text_layout.hpp" -#include "drape_frontend/visual_params.hpp" #include "drape_frontend/intrusive_vector.hpp" #include "drape/attribute_provider.hpp" @@ -32,16 +31,24 @@ class PathTextHandle : public df::TextHandle public: PathTextHandle(m2::SharedSpline const & spl, df::SharedTextLayout const & layout, - float const mercatorOffset, float const depth, + float const mercatorOffset, + float depth, + uint32_t textIndex, uint64_t priority, - ref_ptr textureManager) - : TextHandle(FeatureID(), layout->GetText(), dp::Center, priority, textureManager) + ref_ptr textureManager, + bool isBillboard) + : TextHandle(FeatureID(), layout->GetText(), dp::Center, priority, textureManager, isBillboard) , m_spline(spl) , m_layout(layout) + , m_textIndex(textIndex) + , m_globalOffset(mercatorOffset) , m_depth(depth) { - m_centerPointIter = m_spline.CreateIterator(); - m_centerPointIter.Advance(mercatorOffset); + + m2::Spline::iterator centerPointIter = m_spline.CreateIterator(); + centerPointIter.Advance(m_globalOffset); + m_globalPivot = centerPointIter.m_pos; + m_buffer.resize(4 * m_layout->GetGlyphCount()); } double GetMinScaleInPerspective() const override { return 0.5; } @@ -51,20 +58,70 @@ public: if (!df::TextHandle::Update(screen)) return false; - if (m_normals.empty()) - m_normals.resize(4 * m_layout->GetGlyphCount()); + if (m_buffer.empty()) + m_buffer.resize(4 * m_layout->GetGlyphCount()); + + m2::Spline pixelSpline(m_spline->GetSize()); - return m_layout->CacheDynamicGeometry(m_centerPointIter, m_depth, screen, m_normals); + vector const & globalPoints = m_spline->GetPath(); + for (auto pos : globalPoints) + { + pos = screen.GtoP(pos); + + if (screen.isPerspective()) + { + if (!screen.PixelRect().IsPointInside(pos)) + continue; + + pos = screen.PtoP3d(pos); + } + + pixelSpline.AddPoint(pos); + } + + m2::Spline::iterator centerPointIter; + centerPointIter.Attach(pixelSpline); + + if (screen.isPerspective()) + { + vector offsets; + df::PathTextLayout::CalculatePositions(offsets, pixelSpline.GetLength(), 1.0, + m_layout->GetPixelLength()); + + if (offsets.size() <= m_textIndex) + return false; + + centerPointIter.Advance(offsets[m_textIndex]); + m_globalPivot = screen.PtoG(screen.P3dtoP(centerPointIter.m_pos)); + } + else + { + centerPointIter.Advance(m_globalOffset / screen.GetScale()); + m_globalPivot = screen.PtoG(centerPointIter.m_pos); + } + return m_layout->CacheDynamicGeometry(centerPointIter, m_depth, m_globalPivot, m_buffer); } m2::RectD GetPixelRect(ScreenBase const & screen, bool perspective) const override { - if (perspective) - return GetPixelRectPerspective(screen); + m2::PointD const pixelPivot(screen.GtoP(m_globalPivot)); + + if (perspective) + { + if (IsBillboard()) + { + m2::RectD r = GetPixelRect(screen, false); + m2::PointD pixelPivotPerspective = screen.PtoP3d(pixelPivot); + r.Offset(-pixelPivot); + r.Offset(pixelPivotPerspective); + + return r; + } + return GetPixelRectPerspective(screen); + } - m2::PointD const pixelPivot(screen.GtoP(m_centerPointIter.m_pos)); m2::RectD result; - for (gpu::TextDynamicVertex const & v : m_normals) + for (gpu::TextDynamicVertex const & v : m_buffer) result.Add(pixelPivot + glsl::ToPoint(v.m_normal)); return result; @@ -72,19 +129,29 @@ public: void GetPixelShape(ScreenBase const & screen, Rects & rects, bool perspective) const override { - m2::PointD const pixelPivot(screen.GtoP(m_centerPointIter.m_pos)); - for (size_t quadIndex = 0; quadIndex < m_normals.size(); quadIndex += 4) + m2::PointD const pixelPivot(screen.GtoP(m_globalPivot)); + for (size_t quadIndex = 0; quadIndex < m_buffer.size(); quadIndex += 4) { m2::RectF r; - r.Add(pixelPivot + glsl::ToPoint(m_normals[quadIndex].m_normal)); - r.Add(pixelPivot + glsl::ToPoint(m_normals[quadIndex + 1].m_normal)); - r.Add(pixelPivot + glsl::ToPoint(m_normals[quadIndex + 2].m_normal)); - r.Add(pixelPivot + glsl::ToPoint(m_normals[quadIndex + 3].m_normal)); + r.Add(pixelPivot + glsl::ToPoint(m_buffer[quadIndex].m_normal)); + r.Add(pixelPivot + glsl::ToPoint(m_buffer[quadIndex + 1].m_normal)); + r.Add(pixelPivot + glsl::ToPoint(m_buffer[quadIndex + 2].m_normal)); + r.Add(pixelPivot + glsl::ToPoint(m_buffer[quadIndex + 3].m_normal)); + + if (perspective) + { + if (IsBillboard()) + { + m2::PointD const pxPivotPerspective = screen.PtoP3d(pixelPivot); + + r.Offset(-pixelPivot); + r.Offset(pxPivotPerspective); + } + else + r = m2::RectF(GetPerspectiveRect(m2::RectD(r), screen)); + } m2::RectD const screenRect = perspective ? screen.PixelRectIn3d() : screen.PixelRect(); - if (perspective) - r = m2::RectF(GetPerspectiveRect(m2::RectD(r), screen)); - if (screenRect.IsIntersect(m2::RectD(r))) rects.emplace_back(move(r)); } @@ -105,9 +172,11 @@ public: private: m2::SharedSpline m_spline; - m2::Spline::iterator m_centerPointIter; - float const m_depth; df::SharedTextLayout m_layout; + uint32_t const m_textIndex; + m2::PointD m_globalPivot; + float const m_globalOffset; + float const m_depth; }; } @@ -137,12 +206,13 @@ uint64_t PathTextShape::GetOverlayPriority() const void PathTextShape::DrawPathTextPlain(ref_ptr textures, ref_ptr batcher, unique_ptr && layout, - buffer_vector const & offsets) const + vector const & offsets) const { dp::TextureManager::ColorRegion color; textures->GetColorRegion(m_params.m_textFont.m_color, color); dp::GLState state(gpu::TEXT_PROGRAM, dp::GLState::OverlayLayer); + state.SetProgram3dIndex(gpu::TEXT_BILLBOARD_PROGRAM); state.SetColorTexture(color.GetTexture()); state.SetMaskTexture(layout->GetMaskTexture()); @@ -150,15 +220,13 @@ void PathTextShape::DrawPathTextPlain(ref_ptr textures, gpu::TTextStaticVertexBuffer staticBuffer; gpu::TTextDynamicVertexBuffer dynBuffer; SharedTextLayout layoutPtr(layout.release()); - for (float offset : offsets) + for (size_t textIndex = 0; textIndex < offsets.size(); ++textIndex) { + float offset = offsets[textIndex]; staticBuffer.clear(); dynBuffer.clear(); - Spline::iterator iter = m_spline.CreateIterator(); - iter.Advance(offset); layoutPtr->CacheStaticGeometry(color, staticBuffer); - dynBuffer.resize(staticBuffer.size()); dp::AttributeProvider provider(2, staticBuffer.size()); @@ -167,8 +235,10 @@ void PathTextShape::DrawPathTextPlain(ref_ptr textures, drape_ptr handle = make_unique_dp(m_spline, layoutPtr, offset, m_params.m_depth, + textIndex, GetOverlayPriority(), - textures); + textures, + true); batcher->InsertListOfStrip(state, make_ref(&provider), move(handle), 4); } } @@ -176,7 +246,7 @@ void PathTextShape::DrawPathTextPlain(ref_ptr textures, void PathTextShape::DrawPathTextOutlined(ref_ptr textures, ref_ptr batcher, unique_ptr && layout, - buffer_vector const & offsets) const + vector const & offsets) const { dp::TextureManager::ColorRegion color; dp::TextureManager::ColorRegion outline; @@ -184,6 +254,7 @@ void PathTextShape::DrawPathTextOutlined(ref_ptr textures, textures->GetColorRegion(m_params.m_textFont.m_outlineColor, outline); dp::GLState state(gpu::TEXT_OUTLINED_PROGRAM, dp::GLState::OverlayLayer); + state.SetProgram3dIndex(gpu::TEXT_OUTLINED_BILLBOARD_PROGRAM); state.SetColorTexture(color.GetTexture()); state.SetMaskTexture(layout->GetMaskTexture()); @@ -191,15 +262,13 @@ void PathTextShape::DrawPathTextOutlined(ref_ptr textures, gpu::TTextOutlinedStaticVertexBuffer staticBuffer; gpu::TTextDynamicVertexBuffer dynBuffer; SharedTextLayout layoutPtr(layout.release()); - for (float offset : offsets) + for (size_t textIndex = 0; textIndex < offsets.size(); ++textIndex) { + float offset = offsets[textIndex]; staticBuffer.clear(); dynBuffer.clear(); - Spline::iterator iter = m_spline.CreateIterator(); - iter.Advance(offset); layoutPtr->CacheStaticGeometry(color, outline, staticBuffer); - dynBuffer.resize(staticBuffer.size()); dp::AttributeProvider provider(2, staticBuffer.size()); @@ -208,8 +277,10 @@ void PathTextShape::DrawPathTextOutlined(ref_ptr textures, drape_ptr handle = make_unique_dp(m_spline, layoutPtr, offset, m_params.m_depth, + textIndex, GetOverlayPriority(), - textures); + textures, + true); batcher->InsertListOfStrip(state, make_ref(&provider), move(handle), 4); } } @@ -223,38 +294,12 @@ void PathTextShape::Draw(ref_ptr batcher, ref_ptrGetPixelLength(); - float const pathGlbLength = m_spline->GetLength(); - - // on next readable scale m_scaleGtoP will be twice - if (textLength > pathGlbLength * 2.0 * m_params.m_baseGtoPScale) + vector offsets; + PathTextLayout::CalculatePositions(offsets, m_spline->GetLength(), m_params.m_baseGtoPScale, + layout->GetPixelLength()); + if (offsets.empty()) return; - float const kPathLengthScalar = 0.75; - float const pathLength = kPathLengthScalar * m_params.m_baseGtoPScale * pathGlbLength; - - float const etalonEmpty = max(300 * df::VisualParams::Instance().GetVisualScale(), (double)textLength); - float const minPeriodSize = etalonEmpty + textLength; - float const twoTextAndEmpty = minPeriodSize + textLength; - - buffer_vector offsets; - if (pathLength < twoTextAndEmpty) - { - // if we can't place 2 text and empty part on path - // we place only one text on center of path - offsets.push_back(pathGlbLength / 2.0f); - } - else - { - double const textCount = max(floor(pathLength / minPeriodSize), 1.0); - double const glbTextLen = pathGlbLength / textCount; - for (double offset = 0.5 * glbTextLen; offset < pathGlbLength; offset += glbTextLen) - offsets.push_back(offset); - } - if (m_params.m_textFont.m_outlineColor == dp::Color::Transparent()) DrawPathTextPlain(textures, batcher, move(layout), offsets); else diff --git a/drape_frontend/path_text_shape.hpp b/drape_frontend/path_text_shape.hpp index ea7c0951c4..04cc864584 100644 --- a/drape_frontend/path_text_shape.hpp +++ b/drape_frontend/path_text_shape.hpp @@ -22,11 +22,11 @@ private: void DrawPathTextPlain(ref_ptr textures, ref_ptr batcher, unique_ptr && layout, - buffer_vector const & offests) const; + vector const & offests) const; void DrawPathTextOutlined(ref_ptr textures, ref_ptr batcher, unique_ptr && layout, - buffer_vector const & offsets) const; + vector const & offsets) const; m2::SharedSpline m_spline; PathTextViewParams m_params; diff --git a/drape_frontend/text_handle.cpp b/drape_frontend/text_handle.cpp index cac5e5d2d3..c38c9322ba 100644 --- a/drape_frontend/text_handle.cpp +++ b/drape_frontend/text_handle.cpp @@ -7,8 +7,9 @@ namespace df TextHandle::TextHandle(FeatureID const & id, strings::UniString const & text, dp::Anchor anchor, uint64_t priority, - ref_ptr textureManager) - : OverlayHandle(id, anchor, priority, false) + ref_ptr textureManager, + bool isBillboard) + : OverlayHandle(id, anchor, priority, isBillboard) , m_forceUpdateNormals(false) , m_isLastVisible(false) , m_text(text) @@ -22,7 +23,7 @@ TextHandle::TextHandle(FeatureID const & id, strings::UniString const & text, gpu::TTextDynamicVertexBuffer && normals, bool isBillboard) : OverlayHandle(id, anchor, priority, isBillboard) - , m_normals(move(normals)) + , m_buffer(move(normals)) , m_forceUpdateNormals(false) , m_isLastVisible(false) , m_text(text) @@ -41,12 +42,12 @@ void TextHandle::GetAttributeMutation(ref_ptr mutato TOffsetNode const & node = GetOffsetNode(gpu::TextDynamicVertex::GetDynamicStreamID()); ASSERT(node.first.GetElementSize() == sizeof(gpu::TextDynamicVertex), ()); - ASSERT(node.second.m_count == m_normals.size(), ()); + ASSERT(node.second.m_count == m_buffer.size(), ()); - uint32_t byteCount = m_normals.size() * sizeof(gpu::TextDynamicVertex); + uint32_t byteCount = m_buffer.size() * sizeof(gpu::TextDynamicVertex); void * buffer = mutator->AllocateMutationBuffer(byteCount); if (isVisible) - memcpy(buffer, m_normals.data(), byteCount); + memcpy(buffer, m_buffer.data(), byteCount); else memset(buffer, 0, byteCount); diff --git a/drape_frontend/text_handle.hpp b/drape_frontend/text_handle.hpp index 47f7506626..7524f7eb95 100644 --- a/drape_frontend/text_handle.hpp +++ b/drape_frontend/text_handle.hpp @@ -19,7 +19,8 @@ class TextHandle : public dp::OverlayHandle public: TextHandle(FeatureID const & id, strings::UniString const & text, dp::Anchor anchor, uint64_t priority, - ref_ptr textureManager); + ref_ptr textureManager, + bool isBillboard = false); TextHandle(FeatureID const & id, strings::UniString const & text, dp::Anchor anchor, uint64_t priority, @@ -37,7 +38,7 @@ public: void SetForceUpdateNormals(bool forceUpdate) const; protected: - gpu::TTextDynamicVertexBuffer m_normals; + gpu::TTextDynamicVertexBuffer m_buffer; mutable bool m_forceUpdateNormals; private: diff --git a/drape_frontend/text_layout.cpp b/drape_frontend/text_layout.cpp index af91f939d9..3116d1c114 100644 --- a/drape_frontend/text_layout.cpp +++ b/drape_frontend/text_layout.cpp @@ -1,4 +1,5 @@ #include "drape_frontend/text_layout.hpp" +#include "drape_frontend/visual_params.hpp" #include "drape_frontend/visual_params.hpp" @@ -457,37 +458,36 @@ void PathTextLayout::CacheStaticGeometry(dp::TextureManager::ColorRegion const & } bool PathTextLayout::CacheDynamicGeometry(m2::Spline::iterator const & iter, const float depth, - ScreenBase const & screen, + m2::PointD const & globalPivot, gpu::TTextDynamicVertexBuffer & buffer) const { - float const scalePtoG = screen.GetScale(); - float const glbHalfLength = 0.5 * GetPixelLength() * scalePtoG; + float const halfLength = 0.5 * GetPixelLength(); m2::Spline::iterator beginIter = iter; - beginIter.Advance(-glbHalfLength); + beginIter.Advance(-halfLength); m2::Spline::iterator endIter = iter; - endIter.Advance(glbHalfLength); + endIter.Advance(halfLength); if (beginIter.BeginAgain() || endIter.BeginAgain()) return false; float const halfFontSize = 0.5 * GetPixelHeight(); float advanceSign = 1.0f; m2::Spline::iterator penIter = beginIter; - if (screen.GtoP(beginIter.m_pos).x > screen.GtoP(endIter.m_pos).x) + if (beginIter.m_pos.x > endIter.m_pos.x) { advanceSign = -advanceSign; penIter = endIter; } - glsl::vec2 pxPivot = glsl::ToVec2(screen.GtoP(iter.m_pos)); + glsl::vec2 pxPivot = glsl::ToVec2(iter.m_pos); buffer.resize(4 * m_metrics.size()); for (size_t i = 0; i < m_metrics.size(); ++i) { GlyphRegion const & g = m_metrics[i]; m2::PointF pxSize = m2::PointF(g.GetPixelSize()) * m_textSizeRatio; - m2::PointD const pxBase = screen.GtoP(penIter.m_pos); - m2::PointD const pxShiftBase = screen.GtoP(penIter.m_pos + penIter.m_dir); + m2::PointD const pxBase = penIter.m_pos; + m2::PointD const pxShiftBase = penIter.m_pos + penIter.m_dir; glsl::vec2 tangent = advanceSign * glsl::normalize(glsl::ToVec2(pxShiftBase - pxBase)); glsl::vec2 normal = glsl::normalize(glsl::vec2(-tangent.y, tangent.x)); @@ -501,7 +501,7 @@ bool PathTextLayout::CacheDynamicGeometry(m2::Spline::iterator const & iter, con size_t baseIndex = 4 * i; - glsl::vec3 pivot(glsl::ToVec2(iter.m_pos), depth); + glsl::vec3 pivot(glsl::ToVec2(globalPivot), depth); buffer[baseIndex + 0] = gpu::TextDynamicVertex(pivot, formingVector + normal * bottomVector + tangent * xOffset); buffer[baseIndex + 1] = gpu::TextDynamicVertex(pivot, formingVector + normal * upVector + tangent * xOffset); buffer[baseIndex + 2] = gpu::TextDynamicVertex(pivot, formingVector + normal * bottomVector + tangent * (pxSize.x + xOffset)); @@ -509,7 +509,7 @@ bool PathTextLayout::CacheDynamicGeometry(m2::Spline::iterator const & iter, con float const xAdvance = g.GetAdvanceX() * m_textSizeRatio; glsl::vec2 currentTangent = glsl::ToVec2(penIter.m_dir); - penIter.Advance(advanceSign * xAdvance * scalePtoG); + penIter.Advance(advanceSign * xAdvance); float const dotProduct = glsl::dot(currentTangent, glsl::ToVec2(penIter.m_dir)); if (dotProduct < kValidSplineTurn) return false; @@ -518,6 +518,42 @@ bool PathTextLayout::CacheDynamicGeometry(m2::Spline::iterator const & iter, con return true; } +// static +void PathTextLayout::CalculatePositions(vector & offsets, float splineLength, + float splineScaleToPixel, float textPixelLength) +{ + //we leave a little space on either side of the text that would + //remove the comparison for equality of spline portions + float const kTextBorder = 4.0f; + float const textLength = kTextBorder + textPixelLength; + float const pathGlbLength = splineLength; + + // on next readable scale m_scaleGtoP will be twice + if (textLength > pathGlbLength * 2.0 * splineScaleToPixel) + return; + + float const kPathLengthScalar = 0.75; + float const pathLength = kPathLengthScalar * splineScaleToPixel * pathGlbLength; + + float const etalonEmpty = max(300 * df::VisualParams::Instance().GetVisualScale(), (double)textLength); + float const minPeriodSize = etalonEmpty + textLength; + float const twoTextAndEmpty = minPeriodSize + textLength; + + if (pathLength < twoTextAndEmpty) + { + // if we can't place 2 text and empty part on path + // we place only one text on center of path + offsets.push_back(pathGlbLength / 2.0f); + } + else + { + double const textCount = max(floor(pathLength / minPeriodSize), 1.0); + double const glbTextLen = pathGlbLength / textCount; + for (double offset = 0.5 * glbTextLen; offset < pathGlbLength; offset += glbTextLen) + offsets.push_back(offset); + } +} + /////////////////////////////////////////////////////////////// SharedTextLayout::SharedTextLayout(PathTextLayout * layout) : m_layout(layout) diff --git a/drape_frontend/text_layout.hpp b/drape_frontend/text_layout.hpp index 6322aecf21..211b70df2d 100644 --- a/drape_frontend/text_layout.hpp +++ b/drape_frontend/text_layout.hpp @@ -87,6 +87,9 @@ public: PathTextLayout(strings::UniString const & text, float fontSize, ref_ptr textures); + static void CalculatePositions(vector & offsets, float splineLength, + float splineScaleToPixel, float textPixelLength); + void CacheStaticGeometry(dp::TextureManager::ColorRegion const & colorRegion, dp::TextureManager::ColorRegion const & outlineRegion, gpu::TTextOutlinedStaticVertexBuffer & staticBuffer) const; @@ -96,7 +99,7 @@ public: bool CacheDynamicGeometry(m2::Spline::iterator const & iter, float const depth, - ScreenBase const & screen, + m2::PointD const & globalPivot, gpu::TTextDynamicVertexBuffer & buffer) const; };