Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mapsme/omim.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorr.kuznetsov <r.kuznetsov@corp.mail.ru>2016-08-12 13:01:35 +0300
committerr.kuznetsov <r.kuznetsov@corp.mail.ru>2016-08-23 15:47:43 +0300
commit2810f6b92636d738b244dbcb681ce647dbafc23f (patch)
tree86ecf02297f4a7028698d2f75ae8fe941e553ac0 /drape_frontend
parent135a12cd030a8bf0c62caaae621bc625a8ff1be1 (diff)
Refactored routing rendering
Diffstat (limited to 'drape_frontend')
-rw-r--r--drape_frontend/backend_renderer.cpp17
-rwxr-xr-xdrape_frontend/frontend_renderer.cpp16
-rwxr-xr-xdrape_frontend/frontend_renderer.hpp2
-rw-r--r--drape_frontend/line_shape_helper.cpp28
-rw-r--r--drape_frontend/line_shape_helper.hpp5
-rw-r--r--drape_frontend/message.hpp2
-rw-r--r--drape_frontend/message_subclasses.hpp32
-rw-r--r--drape_frontend/route_builder.cpp43
-rw-r--r--drape_frontend/route_builder.hpp14
-rw-r--r--drape_frontend/route_renderer.cpp371
-rw-r--r--drape_frontend/route_renderer.hpp39
-rw-r--r--drape_frontend/route_shape.cpp386
-rw-r--r--drape_frontend/route_shape.hpp62
13 files changed, 541 insertions, 476 deletions
diff --git a/drape_frontend/backend_renderer.cpp b/drape_frontend/backend_renderer.cpp
index 6b10cd95b1..d7a7af41f5 100644
--- a/drape_frontend/backend_renderer.cpp
+++ b/drape_frontend/backend_renderer.cpp
@@ -46,6 +46,11 @@ BackendRenderer::BackendRenderer(Params const & params)
m_commutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<FlushRouteSignMessage>(move(routeSignData)),
MessagePriority::Normal);
+ }, [this](drape_ptr<RouteArrowsData> && routeArrowsData)
+ {
+ m_commutator->PostMessage(ThreadsCommutator::RenderThread,
+ make_unique_dp<FlushRouteArrowsMessage>(move(routeArrowsData)),
+ MessagePriority::Normal);
});
StartThread();
@@ -240,12 +245,18 @@ void BackendRenderer::AcceptMessage(ref_ptr<Message> message)
m_routeBuilder->BuildSign(msg->GetPosition(), msg->IsStart(), msg->IsValid(), m_texMng);
break;
}
+ case Message::CacheRouteArrows:
+ {
+ ref_ptr<CacheRouteArrowsMessage> msg = message;
+ m_routeBuilder->BuildArrows(msg->GetRouteIndex(), msg->GetBorders(), m_texMng);
+ break;
+ }
case Message::RemoveRoute:
{
ref_ptr<RemoveRouteMessage> msg = message;
-
- // we have to resend the message to FR, because it guaranties that
- // RemoveRouteMessage will be precessed after FlushRouteMessage
+ m_routeBuilder->ClearRouteCache();
+ // We have to resend the message to FR, because it guaranties that
+ // RemoveRouteMessage will be processed after FlushRouteMessage.
m_commutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<RemoveRouteMessage>(msg->NeedDeactivateFollowing()),
MessagePriority::Normal);
diff --git a/drape_frontend/frontend_renderer.cpp b/drape_frontend/frontend_renderer.cpp
index 014a26e1e1..5939d93345 100755
--- a/drape_frontend/frontend_renderer.cpp
+++ b/drape_frontend/frontend_renderer.cpp
@@ -500,6 +500,14 @@ void FrontendRenderer::AcceptMessage(ref_ptr<Message> message)
break;
}
+ case Message::FlushRouteArrows:
+ {
+ ref_ptr<FlushRouteArrowsMessage> msg = message;
+ drape_ptr<RouteArrowsData> routeArrowsData = msg->AcceptRouteArrowsData();
+ m_routeRenderer->SetRouteArrows(move(routeArrowsData), make_ref(m_gpuProgramManager));
+ break;
+ }
+
case Message::RemoveRoute:
{
ref_ptr<RemoveRouteMessage> msg = message;
@@ -1640,6 +1648,7 @@ void FrontendRenderer::PrepareScene(ScreenBase const & modelView)
RefreshPivotTransform(modelView);
m_myPositionController->UpdatePixelPosition(modelView);
+ m_routeRenderer->UpdateRoute(modelView, bind(&FrontendRenderer::OnCacheRouteArrows, this, _1, _2));
}
void FrontendRenderer::UpdateScene(ScreenBase const & modelView)
@@ -1674,6 +1683,13 @@ void FrontendRenderer::EmitModelViewChanged(ScreenBase const & modelView) const
m_modelViewChangedFn(modelView);
}
+void FrontendRenderer::OnCacheRouteArrows(int routeIndex, vector<ArrowBorders> const & borders)
+{
+ m_commutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
+ make_unique_dp<CacheRouteArrowsMessage>(routeIndex, borders),
+ MessagePriority::Normal);
+}
+
FrontendRenderer::RenderLayer::RenderLayerID FrontendRenderer::RenderLayer::GetLayerID(dp::GLState const & state)
{
if (state.GetDepthLayer() == dp::GLState::OverlayLayer)
diff --git a/drape_frontend/frontend_renderer.hpp b/drape_frontend/frontend_renderer.hpp
index 1117f31f8c..ab99ec0954 100755
--- a/drape_frontend/frontend_renderer.hpp
+++ b/drape_frontend/frontend_renderer.hpp
@@ -243,6 +243,8 @@ private:
void ProcessSelection(ref_ptr<SelectObjectMessage> msg);
+ void OnCacheRouteArrows(int routeIndex, vector<ArrowBorders> const & borders);
+
drape_ptr<dp::GpuProgramManager> m_gpuProgramManager;
struct RenderLayer
diff --git a/drape_frontend/line_shape_helper.cpp b/drape_frontend/line_shape_helper.cpp
index 37fad390b3..7a994e4b03 100644
--- a/drape_frontend/line_shape_helper.cpp
+++ b/drape_frontend/line_shape_helper.cpp
@@ -126,7 +126,8 @@ void UpdateNormals(LineSegment * segment, LineSegment * prevSegment, LineSegment
}
void GenerateJoinNormals(dp::LineJoin joinType, glsl::vec2 const & normal1, glsl::vec2 const & normal2,
- float halfWidth, bool isLeft, float widthScalar, vector<glsl::vec2> & normals)
+ float halfWidth, bool isLeft, float widthScalar, vector<glsl::vec2> & normals,
+ vector<glsl::vec2> * uv)
{
float const eps = 1e-5;
if (fabs(glsl::dot(normal1, normal2) - 1.0f) < eps)
@@ -140,6 +141,13 @@ void GenerateJoinNormals(dp::LineJoin joinType, glsl::vec2 const & normal1, glsl
normals.push_back(glsl::vec2(0.0f, 0.0f));
normals.push_back(isLeft ? n1 : n2);
normals.push_back(isLeft ? n2 : n1);
+
+ if (uv != nullptr)
+ {
+ uv->push_back(glsl::vec2(0.5f, 0.5f));
+ uv->push_back(isLeft ? glsl::vec2(0.5f, 0.0f) : glsl::vec2(0.5f, 1.0f));
+ uv->push_back(isLeft ? glsl::vec2(0.5f, 0.0f) : glsl::vec2(0.5f, 1.0f));
+ }
}
else if (joinType == dp::LineJoin::MiterJoin)
{
@@ -155,6 +163,17 @@ void GenerateJoinNormals(dp::LineJoin joinType, glsl::vec2 const & normal1, glsl
normals.push_back(glsl::vec2(0.0f, 0.0f));
normals.push_back(isLeft ? averageNormal : n2);
normals.push_back(isLeft ? n2 : averageNormal);
+
+ if (uv != nullptr)
+ {
+ uv->push_back(glsl::vec2(0.5f, 0.5f));
+ uv->push_back(isLeft ? glsl::vec2(0.5f, 0.0f) : glsl::vec2(0.5f, 1.0f));
+ uv->push_back(isLeft ? glsl::vec2(0.5f, 0.0f) : glsl::vec2(0.5f, 1.0f));
+
+ uv->push_back(glsl::vec2(0.5f, 0.5f));
+ uv->push_back(isLeft ? glsl::vec2(0.5f, 0.0f) : glsl::vec2(0.5f, 1.0f));
+ uv->push_back(isLeft ? glsl::vec2(0.5f, 0.0f) : glsl::vec2(0.5f, 1.0f));
+ }
}
else
{
@@ -176,6 +195,13 @@ void GenerateJoinNormals(dp::LineJoin joinType, glsl::vec2 const & normal1, glsl
normals.push_back(glsl::vec2(0.0f, 0.0f));
normals.push_back(isLeft ? glsl::vec2(n1.x, n1.y) : glsl::vec2(n2.x, n2.y));
normals.push_back(isLeft ? glsl::vec2(n2.x, n2.y) : glsl::vec2(n1.x, n1.y));
+
+ if (uv != nullptr)
+ {
+ uv->push_back(glsl::vec2(0.5f, 0.5f));
+ uv->push_back(isLeft ? glsl::vec2(0.5f, 0.0f) : glsl::vec2(0.5f, 1.0f));
+ uv->push_back(isLeft ? glsl::vec2(0.5f, 0.0f) : glsl::vec2(0.5f, 1.0f));
+ }
}
}
}
diff --git a/drape_frontend/line_shape_helper.hpp b/drape_frontend/line_shape_helper.hpp
index 5014367cc4..e515949053 100644
--- a/drape_frontend/line_shape_helper.hpp
+++ b/drape_frontend/line_shape_helper.hpp
@@ -3,6 +3,8 @@
#include "drape/drape_global.hpp"
#include "drape/glsl_types.hpp"
+#include "geometry/rect2d.hpp"
+
#include "std/vector.hpp"
namespace df
@@ -55,7 +57,8 @@ void ConstructLineSegments(vector<m2::PointD> const & path, vector<LineSegment>
void UpdateNormals(LineSegment * segment, LineSegment * prevSegment, LineSegment * nextSegment);
void GenerateJoinNormals(dp::LineJoin joinType, glsl::vec2 const & normal1, glsl::vec2 const & normal2,
- float halfWidth, bool isLeft, float widthScalar, vector<glsl::vec2> & normals);
+ float halfWidth, bool isLeft, float widthScalar, vector<glsl::vec2> & normals,
+ vector<glsl::vec2> * uv = nullptr);
void GenerateCapNormals(dp::LineCap capType, glsl::vec2 const & normal1, glsl::vec2 const & normal2,
glsl::vec2 const & direction, float halfWidth, bool isStart, vector<glsl::vec2> & normals);
diff --git a/drape_frontend/message.hpp b/drape_frontend/message.hpp
index 204c7bb494..7319c255b6 100644
--- a/drape_frontend/message.hpp
+++ b/drape_frontend/message.hpp
@@ -36,9 +36,11 @@ public:
GetMyPosition,
AddRoute,
CacheRouteSign,
+ CacheRouteArrows,
RemoveRoute,
FlushRoute,
FlushRouteSign,
+ FlushRouteArrows,
FollowRoute,
DeactivateRouteFollowing,
UpdateMapStyle,
diff --git a/drape_frontend/message_subclasses.hpp b/drape_frontend/message_subclasses.hpp
index b947fd29bc..a8bfb5b336 100644
--- a/drape_frontend/message_subclasses.hpp
+++ b/drape_frontend/message_subclasses.hpp
@@ -589,6 +589,24 @@ private:
bool const m_isValid;
};
+class CacheRouteArrowsMessage : public Message
+{
+public:
+ CacheRouteArrowsMessage(int routeIndex, vector<ArrowBorders> const & borders)
+ : m_routeIndex(routeIndex)
+ , m_borders(borders)
+ {}
+
+ Type GetType() const override { return Message::CacheRouteArrows; }
+
+ int GetRouteIndex() const { return m_routeIndex; }
+ vector<ArrowBorders> const & GetBorders() const { return m_borders; }
+
+private:
+ int m_routeIndex;
+ vector<ArrowBorders> m_borders;
+};
+
class RemoveRouteMessage : public Message
{
public:
@@ -618,6 +636,20 @@ private:
drape_ptr<RouteData> m_routeData;
};
+class FlushRouteArrowsMessage : public Message
+{
+public:
+ FlushRouteArrowsMessage(drape_ptr<RouteArrowsData> && routeArrowsData)
+ : m_routeArrowsData(move(routeArrowsData))
+ {}
+
+ Type GetType() const override { return Message::FlushRouteArrows; }
+ drape_ptr<RouteArrowsData> && AcceptRouteArrowsData() { return move(m_routeArrowsData); }
+
+private:
+ drape_ptr<RouteArrowsData> m_routeArrowsData;
+};
+
class FlushRouteSignMessage : public Message
{
public:
diff --git a/drape_frontend/route_builder.cpp b/drape_frontend/route_builder.cpp
index cb49392083..fbf06dcb0b 100644
--- a/drape_frontend/route_builder.cpp
+++ b/drape_frontend/route_builder.cpp
@@ -6,26 +6,25 @@ namespace df
{
RouteBuilder::RouteBuilder(TFlushRouteFn const & flushRouteFn,
- TFlushRouteSignFn const & flushRouteSignFn)
+ TFlushRouteSignFn const & flushRouteSignFn,
+ TFlushRouteArrowsFn const & flushRouteArrowsFn)
: m_flushRouteFn(flushRouteFn)
, m_flushRouteSignFn(flushRouteSignFn)
+ , m_flushRouteArrowsFn(flushRouteArrowsFn)
{}
void RouteBuilder::Build(m2::PolylineD const & routePolyline, vector<double> const & turns,
df::ColorConstant color, df::RoutePattern const & pattern,
ref_ptr<dp::TextureManager> textures)
{
- CommonViewParams params;
- params.m_minVisibleScale = 1;
- params.m_rank = 0;
- params.m_depth = 0.0f;
-
drape_ptr<RouteData> routeData = make_unique_dp<RouteData>();
+ routeData->m_routeIndex = m_routeIndex++;
routeData->m_color = color;
routeData->m_sourcePolyline = routePolyline;
routeData->m_sourceTurns = turns;
routeData->m_pattern = pattern;
- RouteShape(params).Draw(textures, *routeData.get());
+ RouteShape::CacheRoute(textures, *routeData.get());
+ m_routeCache.insert(make_pair(routeData->m_routeIndex, routePolyline));
// Flush route geometry.
GLFunctions::glFlush();
@@ -34,6 +33,11 @@ void RouteBuilder::Build(m2::PolylineD const & routePolyline, vector<double> con
m_flushRouteFn(move(routeData));
}
+void RouteBuilder::ClearRouteCache()
+{
+ m_routeCache.clear();
+}
+
void RouteBuilder::BuildSign(m2::PointD const & pos, bool isStart, bool isValid,
ref_ptr<dp::TextureManager> textures)
{
@@ -42,13 +46,7 @@ void RouteBuilder::BuildSign(m2::PointD const & pos, bool isStart, bool isValid,
routeSignData->m_position = pos;
routeSignData->m_isValid = isValid;
if (isValid)
- {
- CommonViewParams params;
- params.m_minVisibleScale = 1;
- params.m_rank = 0;
- params.m_depth = 0.0f;
- RouteShape(params).CacheRouteSign(textures, *routeSignData.get());
- }
+ RouteShape::CacheRouteSign(textures, *routeSignData.get());
// Flush route sign geometry.
GLFunctions::glFlush();
@@ -57,4 +55,21 @@ void RouteBuilder::BuildSign(m2::PointD const & pos, bool isStart, bool isValid,
m_flushRouteSignFn(move(routeSignData));
}
+void RouteBuilder::BuildArrows(int routeIndex, vector<ArrowBorders> const & borders,
+ ref_ptr<dp::TextureManager> textures)
+{
+ auto it = m_routeCache.find(routeIndex);
+ if (it == m_routeCache.end())
+ return;
+
+ drape_ptr<RouteArrowsData> routeArrowsData = make_unique_dp<RouteArrowsData>();
+ RouteShape::CacheRouteArrows(textures, it->second, borders, *routeArrowsData.get());
+
+ // Flush route arrows geometry.
+ GLFunctions::glFlush();
+
+ if (m_flushRouteArrowsFn != nullptr)
+ m_flushRouteArrowsFn(move(routeArrowsData));
+}
+
} // namespace df
diff --git a/drape_frontend/route_builder.hpp b/drape_frontend/route_builder.hpp
index 8bcf046af0..6851988644 100644
--- a/drape_frontend/route_builder.hpp
+++ b/drape_frontend/route_builder.hpp
@@ -9,6 +9,7 @@
#include "geometry/polyline2d.hpp"
#include "std/function.hpp"
+#include "std/unordered_map.hpp"
namespace df
{
@@ -18,20 +19,31 @@ class RouteBuilder
public:
using TFlushRouteFn = function<void(drape_ptr<RouteData> &&)>;
using TFlushRouteSignFn = function<void(drape_ptr<RouteSignData> &&)>;
+ using TFlushRouteArrowsFn = function<void(drape_ptr<RouteArrowsData> &&)>;
RouteBuilder(TFlushRouteFn const & flushRouteFn,
- TFlushRouteSignFn const & flushRouteSignFn);
+ TFlushRouteSignFn const & flushRouteSignFn,
+ TFlushRouteArrowsFn const & flushRouteArrowsFn);
void Build(m2::PolylineD const & routePolyline, vector<double> const & turns,
df::ColorConstant color, df::RoutePattern const & pattern,
ref_ptr<dp::TextureManager> textures);
+ void BuildArrows(int routeIndex, vector<ArrowBorders> const & borders,
+ ref_ptr<dp::TextureManager> textures);
+
void BuildSign(m2::PointD const & pos, bool isStart, bool isValid,
ref_ptr<dp::TextureManager> textures);
+ void ClearRouteCache();
+
private:
TFlushRouteFn m_flushRouteFn;
TFlushRouteSignFn m_flushRouteSignFn;
+ TFlushRouteArrowsFn m_flushRouteArrowsFn;
+
+ int m_routeIndex = 0;
+ unordered_map<int, m2::PolylineD> m_routeCache;
};
} // namespace df
diff --git a/drape_frontend/route_renderer.cpp b/drape_frontend/route_renderer.cpp
index 446d89b27a..4a7b115980 100644
--- a/drape_frontend/route_renderer.cpp
+++ b/drape_frontend/route_renderer.cpp
@@ -1,4 +1,5 @@
#include "drape_frontend/route_renderer.hpp"
+#include "drape_frontend/message_subclasses.hpp"
#include "drape/glsl_func.hpp"
#include "drape/shader_def.hpp"
@@ -16,11 +17,6 @@ namespace df
namespace
{
-double const kArrowHeightFactor = 96.0 / 36.0;
-double const kArrowAspect = 400.0 / 192.0;
-double const kArrowTailSize = 20.0 / 400.0;
-double const kArrowHeadSize = 124.0 / 400.0;
-
float const kHalfWidthInPixel[] =
{
// 1 2 3 4 5 6 7 8 9 10
@@ -38,50 +34,8 @@ uint8_t const kAlphaValue[] =
};
int const kArrowAppearingZoomLevel = 14;
-
-enum SegmentStatus
-{
- OK = -1,
- NoSegment = -2
-};
-
int const kInvalidGroup = -1;
-// Checks for route segments for intersection with the distance [start; end].
-int CheckForIntersection(double start, double end, vector<RouteSegment> const & segments)
-{
- for (size_t i = 0; i < segments.size(); i++)
- {
- if (segments[i].m_isAvailable)
- continue;
-
- if (start <= segments[i].m_end && end >= segments[i].m_start)
- return i;
- }
- return SegmentStatus::OK;
-}
-
-// Finds the nearest appropriate route segment to the distance [start; end].
-int FindNearestAvailableSegment(double start, double end, vector<RouteSegment> const & segments)
-{
- double const kThreshold = 0.8;
-
- // check if distance intersects unavailable segment
- int index = CheckForIntersection(start, end, segments);
- if (index == SegmentStatus::OK)
- return SegmentStatus::OK;
-
- // find nearest available segment if necessary
- double const len = end - start;
- for (size_t i = index; i < segments.size(); i++)
- {
- double const factor = (segments[i].m_end - segments[i].m_start) / len;
- if (segments[i].m_isAvailable && factor > kThreshold)
- return static_cast<int>(i);
- }
- return SegmentStatus::NoSegment;
-}
-
void ClipBorders(vector<ArrowBorders> & borders)
{
auto invalidBorders = [](ArrowBorders const & borders)
@@ -134,8 +88,32 @@ void BuildBuckets(RouteRenderProperty const & renderProperty, ref_ptr<dp::GpuPro
bucket->GetBuffer()->Build(mng->GetProgram(renderProperty.m_state.GetProgramIndex()));
}
+bool AreEqualArrowBorders(vector<ArrowBorders> const & borders1, vector<ArrowBorders> const & borders2)
+{
+ if (borders1.size() != borders2.size())
+ return false;
+
+ for (size_t i = 0; i < borders1.size(); i++)
+ {
+ if (borders1[i].m_groupIndex != borders2[i].m_groupIndex)
+ return false;
+ }
+
+ double const kDistanceEps = 1e-5;
+ for (size_t i = 0; i < borders1.size(); i++)
+ {
+ if (fabs(borders1[i].m_startDistance - borders2[i].m_startDistance) > kDistanceEps)
+ return false;
+
+ if (fabs(borders1[i].m_endDistance - borders2[i].m_endDistance) > kDistanceEps)
+ return false;
+ }
+
+ return true;
}
+} // namespace
+
RouteRenderer::RouteRenderer()
: m_distanceFromBegin(0.0)
{}
@@ -162,67 +140,144 @@ void RouteRenderer::InterpolateByZoom(ScreenBase const & screen, float & halfWid
}
}
-void RouteRenderer::RenderRoute(ScreenBase const & screen, ref_ptr<dp::GpuProgramManager> mng,
- dp::UniformValuesStorage const & commonUniforms)
+void RouteRenderer::UpdateRoute(ScreenBase const & screen, TCacheRouteArrowsCallback const & callback)
{
+ ASSERT(callback != nullptr, ());
+
if (!m_routeData)
return;
- // interpolate values by zoom level
+ // Interpolate values by zoom level.
double zoom = 0.0;
- float halfWidth = 0.0;
- float alpha = 0.0;
- InterpolateByZoom(screen, halfWidth, alpha, zoom);
+ InterpolateByZoom(screen, m_currentHalfWidth, m_currentAlpha, zoom);
+
+ // Update arrows.
+ if (zoom >= kArrowAppearingZoomLevel && !m_routeData->m_sourceTurns.empty())
+ {
+ // Calculate arrow mercator length.
+ double glbHalfLen = 0.5 * kArrowSize;
+ double const glbHalfTextureWidth = m_currentHalfWidth * kArrowHeightFactor * screen.GetScale();
+ double const glbHalfTextureLen = glbHalfTextureWidth * kArrowAspect;
+ if (glbHalfLen < glbHalfTextureLen)
+ glbHalfLen = glbHalfTextureLen;
+
+ double const glbArrowHead = 2.0 * kArrowHeadSize * glbHalfTextureLen;
+ double const glbArrowTail = 2.0 * kArrowTailSize * glbHalfTextureLen;
+ double const glbMinArrowSize = glbArrowHead + glbArrowTail;
+
+ double const kExtendCoef = 1.1;
+ m2::RectD screenRect = screen.ClipRect();
+ screenRect.Scale(kExtendCoef);
+
+ // Calculate arrow borders.
+ vector<ArrowBorders> newArrowBorders;
+ newArrowBorders.reserve(m_routeData->m_sourceTurns.size());
+ for (size_t i = 0; i < m_routeData->m_sourceTurns.size(); i++)
+ {
+ ArrowBorders arrowBorders;
+ arrowBorders.m_groupIndex = static_cast<int>(i);
+ arrowBorders.m_startDistance = max(0.0, m_routeData->m_sourceTurns[i] - glbHalfLen * 0.8);
+ arrowBorders.m_endDistance = min(m_routeData->m_length, m_routeData->m_sourceTurns[i] + glbHalfLen * 1.2);
+
+ if ((arrowBorders.m_endDistance - arrowBorders.m_startDistance) < glbMinArrowSize ||
+ arrowBorders.m_startDistance < m_distanceFromBegin)
+ continue;
+
+ m2::PointD pt = m_routeData->m_sourcePolyline.GetPointByDistance(arrowBorders.m_startDistance);
+ if (screenRect.IsPointInside(pt))
+ {
+ newArrowBorders.push_back(arrowBorders);
+ continue;
+ }
+
+ pt = m_routeData->m_sourcePolyline.GetPointByDistance(arrowBorders.m_endDistance);
+ if (screenRect.IsPointInside(pt))
+ {
+ newArrowBorders.push_back(arrowBorders);
+ continue;
+ }
+ }
+
+ // Merge intersected borders and clip them.
+ MergeAndClipBorders(newArrowBorders);
+
+ // Process head and tail.
+ for (ArrowBorders & borders : newArrowBorders)
+ {
+ borders.m_startDistance += glbArrowTail;
+ borders.m_endDistance -= glbArrowHead;
+ }
+
+ if (newArrowBorders.empty())
+ {
+ // Clear arrows.
+ m_arrowBorders.clear();
+ m_routeArrows.reset();
+ }
+ else if (!AreEqualArrowBorders(newArrowBorders, m_arrowBorders))
+ {
+ m_arrowBorders = move(newArrowBorders);
+ callback(m_routeData->m_routeIndex, m_arrowBorders);
+ }
+ }
+ else
+ {
+ m_routeArrows.reset();
+ }
+}
+
+void RouteRenderer::RenderRoute(ScreenBase const & screen, ref_ptr<dp::GpuProgramManager> mng,
+ dp::UniformValuesStorage const & commonUniforms)
+{
+ if (!m_routeData)
+ return;
- // render route
+ // Render route.
{
dp::GLState const & state = m_routeData->m_route.m_state;
- // set up uniforms
+ // Set up uniforms.
dp::UniformValuesStorage uniforms = commonUniforms;
glsl::vec4 const color = glsl::ToVec4(df::GetColorConstant(GetStyleReader().GetCurrentStyle(),
m_routeData->m_color));
- uniforms.SetFloatValue("u_color", color.r, color.g, color.b, alpha);
+ uniforms.SetFloatValue("u_color", color.r, color.g, color.b, m_currentAlpha);
double const screenScale = screen.GetScale();
- uniforms.SetFloatValue("u_routeParams", halfWidth, halfWidth * screenScale, m_distanceFromBegin);
+ uniforms.SetFloatValue("u_routeParams", m_currentHalfWidth, m_currentHalfWidth * screenScale, m_distanceFromBegin);
if (m_routeData->m_pattern.m_isDashed)
{
- uniforms.SetFloatValue("u_pattern", halfWidth * m_routeData->m_pattern.m_dashLength * screenScale,
- halfWidth * m_routeData->m_pattern.m_gapLength * screenScale);
+ uniforms.SetFloatValue("u_pattern", m_currentHalfWidth * m_routeData->m_pattern.m_dashLength * screenScale,
+ m_currentHalfWidth * m_routeData->m_pattern.m_gapLength * screenScale);
}
- // set up shaders and apply uniforms
+ // Set up shaders and apply uniforms.
ref_ptr<dp::GpuProgram> prg = mng->GetProgram(m_routeData->m_pattern.m_isDashed ?
gpu::ROUTE_DASH_PROGRAM : gpu::ROUTE_PROGRAM);
prg->Bind();
dp::ApplyState(state, prg);
dp::ApplyUniforms(uniforms, prg);
- // render routes
+ // Render buckets.
for (drape_ptr<dp::RenderBucket> const & bucket : m_routeData->m_route.m_buckets)
bucket->Render();
}
- // render arrows
- if (zoom >= kArrowAppearingZoomLevel && !m_routeData->m_arrows.empty())
+ // Render arrows.
+ if (m_routeArrows != nullptr)
{
- dp::GLState const & state = m_routeData->m_arrows.front()->m_arrow.m_state;
+ dp::GLState const & state = m_routeArrows->m_arrows.m_state;
// set up shaders and apply common uniforms
dp::UniformValuesStorage uniforms = commonUniforms;
- uniforms.SetFloatValue("u_textureRect", m_routeData->m_arrowTextureRect.minX(),
- m_routeData->m_arrowTextureRect.minY(),
- m_routeData->m_arrowTextureRect.maxX(),
- m_routeData->m_arrowTextureRect.maxY());
+ uniforms.SetFloatValue("u_arrowHalfWidth", m_currentHalfWidth * kArrowHeightFactor);
+ uniforms.SetFloatValue("u_opacity", 1.0f);
ref_ptr<dp::GpuProgram> prg = mng->GetProgram(gpu::ROUTE_ARROW_PROGRAM);
prg->Bind();
dp::ApplyState(state, prg);
dp::ApplyUniforms(uniforms, prg);
-
- for (drape_ptr<ArrowRenderProperty> & property : m_routeData->m_arrows)
- RenderArrow(prg, property, halfWidth, screen);
+ for (drape_ptr<dp::RenderBucket> const & bucket : m_routeArrows->m_arrows.m_buckets)
+ bucket->Render();
}
}
@@ -265,54 +320,12 @@ void RouteRenderer::RenderRouteSign(drape_ptr<RouteSignData> const & sign, Scree
}
}
-void RouteRenderer::RenderArrow(ref_ptr<dp::GpuProgram> prg, drape_ptr<ArrowRenderProperty> const & property,
- float halfWidth, ScreenBase const & screen)
-{
- double const arrowHalfWidth = halfWidth * kArrowHeightFactor;
- double const glbArrowHalfWidth = arrowHalfWidth * screen.GetScale();
- double const textureWidth = 2.0 * arrowHalfWidth * kArrowAspect;
-
- dp::UniformValuesStorage uniformStorage;
- uniformStorage.SetFloatValue("u_routeParams", arrowHalfWidth, glbArrowHalfWidth, m_distanceFromBegin);
-
- // calculate arrows
- CalculateArrowBorders(property, kArrowSize, screen.GetScale(), textureWidth, glbArrowHalfWidth);
-
- // split arrow's data by 16-elements buckets
- array<float, 16> borders;
- borders.fill(0.0f);
- size_t const elementsCount = borders.size();
-
- size_t index = 0;
- for (size_t i = 0; i < m_arrowBorders.size(); i++)
- {
- borders[index++] = m_arrowBorders[i].m_startDistance;
- borders[index++] = m_arrowBorders[i].m_startTexCoord;
- borders[index++] = m_arrowBorders[i].m_endDistance - m_arrowBorders[i].m_startDistance;
- borders[index++] = m_arrowBorders[i].m_endTexCoord;
-
- // render arrow's parts
- if (index == elementsCount || i == m_arrowBorders.size() - 1)
- {
- index = 0;
- uniformStorage.SetMatrix4x4Value("u_arrowBorders", borders.data());
- borders.fill(0.0f);
-
- dp::ApplyUniforms(uniformStorage, prg);
- for (drape_ptr<dp::RenderBucket> const & bucket : property->m_arrow.m_buckets)
- bucket->Render();
- }
- }
-}
-
void RouteRenderer::SetRouteData(drape_ptr<RouteData> && routeData, ref_ptr<dp::GpuProgramManager> mng)
{
m_routeData = move(routeData);
+ m_arrowBorders.clear();
BuildBuckets(m_routeData->m_route, mng);
- for (drape_ptr<ArrowRenderProperty> const & arrow : m_routeData->m_arrows)
- BuildBuckets(arrow->m_arrow, mng);
-
m_distanceFromBegin = 0.0;
}
@@ -357,13 +370,20 @@ drape_ptr<RouteData> const & RouteRenderer::GetRouteData() const
return m_routeData;
}
+void RouteRenderer::SetRouteArrows(drape_ptr<RouteArrowsData> && routeArrowsData,
+ ref_ptr<dp::GpuProgramManager> mng)
+{
+ m_routeArrows = move(routeArrowsData);
+ BuildBuckets(m_routeArrows->m_arrows, mng);
+}
+
void RouteRenderer::Clear(bool keepDistanceFromBegin)
{
m_routeData.reset();
m_startRouteSign.reset();
m_finishRouteSign.reset();
m_arrowBorders.clear();
- m_routeSegments.clear();
+ m_routeArrows.reset();
if (!keepDistanceFromBegin)
m_distanceFromBegin = 0.0;
@@ -374,125 +394,4 @@ void RouteRenderer::UpdateDistanceFromBegin(double distanceFromBegin)
m_distanceFromBegin = distanceFromBegin;
}
-void RouteRenderer::ApplyJoinsBounds(drape_ptr<ArrowRenderProperty> const & property, double joinsBoundsScalar,
- double glbHeadLength, vector<ArrowBorders> & arrowBorders)
-{
- m_routeSegments.clear();
- m_routeSegments.reserve(2 * property->m_joinsBounds.size() + 1);
-
- double const length = property->m_end - property->m_start;
-
- // construct route's segments
- m_routeSegments.emplace_back(0.0, 0.0, true /* m_isAvailable */);
- for (size_t i = 0; i < property->m_joinsBounds.size(); i++)
- {
- double const start = property->m_joinsBounds[i].m_offset +
- property->m_joinsBounds[i].m_start * joinsBoundsScalar;
-
- double const end = property->m_joinsBounds[i].m_offset +
- property->m_joinsBounds[i].m_end * joinsBoundsScalar;
-
- m_routeSegments.back().m_end = start;
- m_routeSegments.emplace_back(start, end, false /* m_isAvailable */);
-
- m_routeSegments.emplace_back(end, 0.0, true /* m_isAvailable */);
- }
- m_routeSegments.back().m_end = length;
-
- // shift head of arrow if necessary
- bool needMerge = false;
- for (size_t i = 0; i < arrowBorders.size(); i++)
- {
- int headIndex = FindNearestAvailableSegment(arrowBorders[i].m_endDistance - glbHeadLength,
- arrowBorders[i].m_endDistance, m_routeSegments);
- if (headIndex != SegmentStatus::OK)
- {
- if (headIndex != SegmentStatus::NoSegment)
- {
- ASSERT_GREATER_OR_EQUAL(headIndex, 0, ());
- double const restDist = length - m_routeSegments[headIndex].m_start;
- if (restDist >= glbHeadLength)
- arrowBorders[i].m_endDistance = min(length, m_routeSegments[headIndex].m_start + glbHeadLength);
- else
- arrowBorders[i].m_groupIndex = kInvalidGroup;
- }
- else
- {
- arrowBorders[i].m_groupIndex = kInvalidGroup;
- }
- needMerge = true;
- }
- }
-
- // merge intersected borders
- if (needMerge)
- MergeAndClipBorders(arrowBorders);
-}
-
-void RouteRenderer::CalculateArrowBorders(drape_ptr<ArrowRenderProperty> const & property, double arrowLength,
- double scale, double arrowTextureWidth, double joinsBoundsScalar)
-{
- ASSERT(!property->m_turns.empty(), ());
-
- double halfLen = 0.5 * arrowLength;
- double const glbTextureWidth = arrowTextureWidth * scale;
- double const glbTailLength = kArrowTailSize * glbTextureWidth;
- double const glbHeadLength = kArrowHeadSize * glbTextureWidth;
-
- int const kArrowPartsCount = 3;
- m_arrowBorders.clear();
- m_arrowBorders.reserve(property->m_turns.size() * kArrowPartsCount);
-
- double const halfTextureWidth = 0.5 * glbTextureWidth;
- if (halfLen < halfTextureWidth)
- halfLen = halfTextureWidth;
-
- // initial filling
- for (size_t i = 0; i < property->m_turns.size(); i++)
- {
- ArrowBorders arrowBorders;
- arrowBorders.m_groupIndex = (int)i;
- arrowBorders.m_startDistance = max(0.0, property->m_turns[i] - halfLen * 0.8);
- arrowBorders.m_endDistance = min(property->m_end - property->m_start, property->m_turns[i] + halfLen * 1.2);
-
- if (arrowBorders.m_startDistance + property->m_start < m_distanceFromBegin)
- continue;
-
- m_arrowBorders.push_back(arrowBorders);
- }
-
- // merge intersected borders and clip them
- MergeAndClipBorders(m_arrowBorders);
-
- // apply joins bounds to prevent draw arrow's head on a join
- ApplyJoinsBounds(property, joinsBoundsScalar, glbHeadLength, m_arrowBorders);
-
- // divide to parts of arrow
- size_t const bordersSize = m_arrowBorders.size();
- for (size_t i = 0; i < bordersSize; i++)
- {
- float const startDistance = m_arrowBorders[i].m_startDistance;
- float const endDistance = m_arrowBorders[i].m_endDistance;
-
- m_arrowBorders[i].m_endDistance = startDistance + glbTailLength;
- m_arrowBorders[i].m_startTexCoord = 0.0;
- m_arrowBorders[i].m_endTexCoord = kArrowTailSize;
-
- ArrowBorders arrowHead;
- arrowHead.m_startDistance = endDistance - glbHeadLength;
- arrowHead.m_endDistance = endDistance;
- arrowHead.m_startTexCoord = 1.0 - kArrowHeadSize;
- arrowHead.m_endTexCoord = 1.0;
- m_arrowBorders.push_back(arrowHead);
-
- ArrowBorders arrowBody;
- arrowBody.m_startDistance = m_arrowBorders[i].m_endDistance;
- arrowBody.m_endDistance = arrowHead.m_startDistance;
- arrowBody.m_startTexCoord = m_arrowBorders[i].m_endTexCoord;
- arrowBody.m_endTexCoord = arrowHead.m_startTexCoord;
- m_arrowBorders.push_back(arrowBody);
- }
-}
-
} // namespace df
-
diff --git a/drape_frontend/route_renderer.hpp b/drape_frontend/route_renderer.hpp
index 5f3f32603c..a9f530704c 100644
--- a/drape_frontend/route_renderer.hpp
+++ b/drape_frontend/route_renderer.hpp
@@ -10,33 +10,15 @@
namespace df
{
-struct ArrowBorders
-{
- double m_startDistance = 0;
- double m_endDistance = 0;
- float m_startTexCoord = 0;
- float m_endTexCoord = 1;
- int m_groupIndex = 0;
-};
-
-struct RouteSegment
-{
- double m_start = 0;
- double m_end = 0;
- bool m_isAvailable = false;
-
- RouteSegment(double start, double end, bool isAvailable)
- : m_start(start)
- , m_end(end)
- , m_isAvailable(isAvailable)
- {}
-};
-
class RouteRenderer final
{
public:
+ using TCacheRouteArrowsCallback = function<void(int, vector<ArrowBorders> const &)>;
+
RouteRenderer();
+ void UpdateRoute(ScreenBase const & screen, TCacheRouteArrowsCallback const & callback);
+
void RenderRoute(ScreenBase const & screen, ref_ptr<dp::GpuProgramManager> mng,
dp::UniformValuesStorage const & commonUniforms);
@@ -50,17 +32,13 @@ public:
drape_ptr<RouteSignData> const & GetStartPoint() const;
drape_ptr<RouteSignData> const & GetFinishPoint() const;
+ void SetRouteArrows(drape_ptr<RouteArrowsData> && routeArrowsData, ref_ptr<dp::GpuProgramManager> mng);
+
void Clear(bool keepDistanceFromBegin = false);
void UpdateDistanceFromBegin(double distanceFromBegin);
private:
- void CalculateArrowBorders(drape_ptr<ArrowRenderProperty> const & property, double arrowLength,
- double scale, double arrowTextureWidth, double joinsBoundsScalar);
- void ApplyJoinsBounds(drape_ptr<ArrowRenderProperty> const & property, double joinsBoundsScalar,
- double glbHeadLength, vector<ArrowBorders> & arrowBorders);
- void RenderArrow(ref_ptr<dp::GpuProgram> prg, drape_ptr<ArrowRenderProperty> const & property,
- float halfWidth, ScreenBase const & screen);
void InterpolateByZoom(ScreenBase const & screen, float & halfWidth, float & alpha, double & zoom) const;
void RenderRouteSign(drape_ptr<RouteSignData> const & sign, ScreenBase const & screen,
ref_ptr<dp::GpuProgramManager> mng, dp::UniformValuesStorage const & commonUniforms);
@@ -69,10 +47,13 @@ private:
drape_ptr<RouteData> m_routeData;
vector<ArrowBorders> m_arrowBorders;
- vector<RouteSegment> m_routeSegments;
+ drape_ptr<RouteArrowsData> m_routeArrows;
drape_ptr<RouteSignData> m_startRouteSign;
drape_ptr<RouteSignData> m_finishRouteSign;
+
+ float m_currentHalfWidth = 0.0f;
+ float m_currentAlpha = 0.0f;
};
} // namespace df
diff --git a/drape_frontend/route_shape.cpp b/drape_frontend/route_shape.cpp
index 8a16984113..5acafd0e21 100644
--- a/drape_frontend/route_shape.cpp
+++ b/drape_frontend/route_shape.cpp
@@ -1,5 +1,4 @@
#include "drape_frontend/route_shape.hpp"
-
#include "drape_frontend/line_shape_helper.hpp"
#include "drape/attribute_provider.hpp"
@@ -21,76 +20,11 @@ float const kLeftSide = 1.0;
float const kCenter = 0.0;
float const kRightSide = -1.0;
-float const kArrowsGeometrySegmentLength = 0.5;
-
void GetArrowTextureRegion(ref_ptr<dp::TextureManager> textures, dp::TextureManager::SymbolRegion & region)
{
textures->GetSymbolRegion("route-arrow", region);
}
-void ClipArrowToSegments(vector<double> const & turns, RouteData & routeData)
-{
- int const cnt = static_cast<int>(routeData.m_length / kArrowsGeometrySegmentLength) + 1;
- routeData.m_arrows.reserve(cnt);
- for (int i = 0; i < cnt; ++i)
- {
- double const start = i * kArrowsGeometrySegmentLength;
- double const end = (i + 1) * kArrowsGeometrySegmentLength;
-
- drape_ptr<ArrowRenderProperty> arrowRenderProperty = make_unique_dp<ArrowRenderProperty>();
-
- // looking for corresponding turns
- int startTurnIndex = -1;
- int endTurnIndex = -1;
- for (size_t j = 0; j < turns.size(); ++j)
- {
- if (turns[j] >= start && turns[j] < end)
- {
- if (startTurnIndex < 0)
- startTurnIndex = j;
-
- if (startTurnIndex >= 0)
- endTurnIndex = j;
-
- arrowRenderProperty->m_turns.push_back(turns[j]);
- }
- }
-
- if (startTurnIndex < 0 || endTurnIndex < 0)
- continue;
-
- // start of arrow segment
- if (startTurnIndex != 0)
- {
- double d = max(0.5 * (turns[startTurnIndex] + turns[startTurnIndex - 1]),
- turns[startTurnIndex] - kArrowSize);
- arrowRenderProperty->m_start = max(0.0, d);
- }
- else
- {
- arrowRenderProperty->m_start = max(0.0, turns[startTurnIndex] - kArrowSize);
- }
-
- // end of arrow segment
- if (endTurnIndex + 1 != turns.size())
- {
- double d = min(0.5 * (turns[endTurnIndex] + turns[endTurnIndex + 1]),
- turns[endTurnIndex] + kArrowSize);
- arrowRenderProperty->m_end = min(routeData.m_length, d);
- }
- else
- {
- arrowRenderProperty->m_end = min(routeData.m_length, turns[endTurnIndex] + kArrowSize);
- }
-
- // rescale turns
- for (size_t j = 0; j < arrowRenderProperty->m_turns.size(); ++j)
- arrowRenderProperty->m_turns[j] -= arrowRenderProperty->m_start;
-
- routeData.m_arrows.push_back(move(arrowRenderProperty));
- }
-}
-
vector<m2::PointD> CalculatePoints(m2::PolylineD const & polyline, double start, double end)
{
vector<m2::PointD> result;
@@ -107,14 +41,14 @@ vector<m2::PointD> CalculatePoints(m2::PolylineD const & polyline, double start,
bool started = false;
for (size_t i = 0; i + 1 < path.size(); i++)
{
- double dist = (path[i + 1] - path[i]).Length();
+ double const dist = (path[i + 1] - path[i]).Length();
if (fabs(dist) < 1e-5)
continue;
- double l = len + dist;
+ double const l = len + dist;
if (!started && start >= len && start <= l)
{
- double k = (start - len) / dist;
+ double const k = (start - len) / dist;
addIfNotExist(path[i] + (path[i + 1] - path[i]) * k);
started = true;
}
@@ -126,7 +60,7 @@ vector<m2::PointD> CalculatePoints(m2::PolylineD const & polyline, double start,
if (end >= len && end <= l)
{
- double k = (end - len) / dist;
+ double const k = (end - len) / dist;
addIfNotExist(path[i] + (path[i + 1] - path[i]) * k);
break;
}
@@ -139,51 +73,74 @@ vector<m2::PointD> CalculatePoints(m2::PolylineD const & polyline, double start,
return result;
}
+void GenerateJoinsTriangles(glsl::vec3 const & pivot, vector<glsl::vec2> const & normals,
+ glsl::vec2 const & length, bool isLeft, RouteShape::TGeometryBuffer & joinsGeometry)
+{
+ float const kEps = 1e-5;
+ size_t const trianglesCount = normals.size() / 3;
+ float const side = isLeft ? kLeftSide : kRightSide;
+ for (int j = 0; j < trianglesCount; j++)
+ {
+ glsl::vec3 const len1 = glsl::vec3(length.x, length.y, glsl::length(normals[3 * j]) < kEps ? kCenter : side);
+ glsl::vec3 const len2 = glsl::vec3(length.x, length.y, glsl::length(normals[3 * j + 1]) < kEps ? kCenter : side);
+ glsl::vec3 const len3 = glsl::vec3(length.x, length.y, glsl::length(normals[3 * j + 2]) < kEps ? kCenter : side);
+
+ joinsGeometry.push_back(RouteShape::RV(pivot, normals[3 * j], len1));
+ joinsGeometry.push_back(RouteShape::RV(pivot, normals[3 * j + 1], len2));
+ joinsGeometry.push_back(RouteShape::RV(pivot, normals[3 * j + 2], len3));
+ }
}
-RouteShape::RouteShape(CommonViewParams const & params)
- : m_params(params)
-{}
+glsl::vec2 GetUV(m2::RectF const & texRect, float normU, float normV)
+{
+ return glsl::vec2(texRect.minX() * (1.0f - normU) + texRect.maxX() * normU,
+ texRect.minY() * (1.0f - normV) + texRect.maxY() * normV);
+}
-void RouteShape::PrepareGeometry(bool isRoute, vector<m2::PointD> const & path,
- TGeometryBuffer & geometry, TGeometryBuffer & joinsGeometry,
- vector<RouteJoinBounds> & joinsBounds, double & outputLength)
+glsl::vec2 GetUV(m2::RectF const & texRect, glsl::vec2 const & uv)
{
- ASSERT(path.size() > 1, ());
+ return GetUV(texRect, uv.x, uv.y);
+}
- auto const generateTriangles = [&joinsGeometry](glsl::vec3 const & pivot, vector<glsl::vec2> const & normals,
- glsl::vec2 const & length, bool isLeft)
+void GenerateArrowsTriangles(glsl::vec4 const & pivot, vector<glsl::vec2> const & normals,
+ m2::RectF const & texRect, vector<glsl::vec2> const & uv,
+ bool normalizedUV, RouteShape::TArrowGeometryBuffer & joinsGeometry)
+{
+ size_t const trianglesCount = normals.size() / 3;
+ for (int j = 0; j < trianglesCount; j++)
{
- float const eps = 1e-5;
- size_t const trianglesCount = normals.size() / 3;
- float const side = isLeft ? kLeftSide : kRightSide;
- for (int j = 0; j < trianglesCount; j++)
- {
- glsl::vec3 const len1 = glsl::vec3(length.x, length.y, glsl::length(normals[3 * j]) < eps ? kCenter : side);
- glsl::vec3 const len2 = glsl::vec3(length.x, length.y, glsl::length(normals[3 * j + 1]) < eps ? kCenter : side);
- glsl::vec3 const len3 = glsl::vec3(length.x, length.y, glsl::length(normals[3 * j + 2]) < eps ? kCenter : side);
+ joinsGeometry.push_back(RouteShape::AV(pivot, normals[3 * j],
+ normalizedUV ? GetUV(texRect, uv[3 * j]) : uv[3 * j]));
+ joinsGeometry.push_back(RouteShape::AV(pivot, normals[3 * j + 1],
+ normalizedUV ? GetUV(texRect, uv[3 * j + 1]) : uv[3 * j + 1]));
+ joinsGeometry.push_back(RouteShape::AV(pivot, normals[3 * j + 2],
+ normalizedUV ? GetUV(texRect, uv[3 * j + 2]) : uv[3 * j + 2]));
+ }
+}
- joinsGeometry.push_back(RV(pivot, normals[3 * j], len1));
- joinsGeometry.push_back(RV(pivot, normals[3 * j + 1], len2));
- joinsGeometry.push_back(RV(pivot, normals[3 * j + 2], len3));
- }
- };
+} // namespace
- // constuct segments
+void RouteShape::PrepareGeometry(vector<m2::PointD> const & path, TGeometryBuffer & geometry,
+ TGeometryBuffer & joinsGeometry, double & outputLength)
+{
+ ASSERT(path.size() > 1, ());
+
+ // Construct segments.
vector<LineSegment> segments;
segments.reserve(path.size() - 1);
ConstructLineSegments(path, segments);
- // build geometry
+ // Build geometry.
float length = 0;
+ float const kDepth = 0.0f;
for (size_t i = 0; i < segments.size(); i++)
{
UpdateNormals(&segments[i], (i > 0) ? &segments[i - 1] : nullptr,
(i < segments.size() - 1) ? &segments[i + 1] : nullptr);
- // generate main geometry
- glsl::vec3 const startPivot = glsl::vec3(segments[i].m_points[StartPoint], m_params.m_depth);
- glsl::vec3 const endPivot = glsl::vec3(segments[i].m_points[EndPoint], m_params.m_depth);
+ // Generate main geometry.
+ glsl::vec3 const startPivot = glsl::vec3(segments[i].m_points[StartPoint], kDepth);
+ glsl::vec3 const endPivot = glsl::vec3(segments[i].m_points[EndPoint], kDepth);
float const endLength = length + glsl::length(segments[i].m_points[EndPoint] - segments[i].m_points[StartPoint]);
@@ -207,7 +164,7 @@ void RouteShape::PrepareGeometry(bool isRoute, vector<m2::PointD> const & path,
geometry.push_back(RV(endPivot, rightNormalEnd, glsl::vec3(endLength, projRightEnd, kRightSide)));
geometry.push_back(RV(endPivot, glsl::vec2(0, 0), glsl::vec3(endLength, 0, kCenter)));
- // generate joins
+ // Generate joins.
if (segments[i].m_generateJoin && i < segments.size() - 1)
{
glsl::vec2 n1 = segments[i].m_hasLeftJoin[EndPoint] ? segments[i].m_leftNormals[EndPoint] :
@@ -222,12 +179,12 @@ void RouteShape::PrepareGeometry(bool isRoute, vector<m2::PointD> const & path,
normals.reserve(24);
GenerateJoinNormals(dp::RoundJoin, n1, n2, 1.0f, segments[i].m_hasLeftJoin[EndPoint], widthScalar, normals);
- generateTriangles(glsl::vec3(segments[i].m_points[EndPoint], m_params.m_depth), normals,
- glsl::vec2(endLength, 0), segments[i].m_hasLeftJoin[EndPoint]);
+ GenerateJoinsTriangles(glsl::vec3(segments[i].m_points[EndPoint], kDepth), normals,
+ glsl::vec2(endLength, 0), segments[i].m_hasLeftJoin[EndPoint], joinsGeometry);
}
- // generate caps
- if (isRoute && i == 0)
+ // Generate caps.
+ if (i == 0)
{
vector<glsl::vec2> normals;
normals.reserve(24);
@@ -235,11 +192,11 @@ void RouteShape::PrepareGeometry(bool isRoute, vector<m2::PointD> const & path,
segments[i].m_rightNormals[StartPoint], -segments[i].m_tangent,
1.0f, true /* isStart */, normals);
- generateTriangles(glsl::vec3(segments[i].m_points[StartPoint], m_params.m_depth), normals,
- glsl::vec2(length, 0), true);
+ GenerateJoinsTriangles(glsl::vec3(segments[i].m_points[StartPoint], kDepth), normals,
+ glsl::vec2(length, 0), true, joinsGeometry);
}
- if (isRoute && i == segments.size() - 1)
+ if (i == segments.size() - 1)
{
vector<glsl::vec2> normals;
normals.reserve(24);
@@ -247,36 +204,132 @@ void RouteShape::PrepareGeometry(bool isRoute, vector<m2::PointD> const & path,
segments[i].m_rightNormals[EndPoint], segments[i].m_tangent,
1.0f, false /* isStart */, normals);
- generateTriangles(glsl::vec3(segments[i].m_points[EndPoint], m_params.m_depth), normals,
- glsl::vec2(endLength, 0), true);
+ GenerateJoinsTriangles(glsl::vec3(segments[i].m_points[EndPoint], kDepth), normals,
+ glsl::vec2(endLength, 0), true, joinsGeometry);
}
length = endLength;
}
- outputLength = length;
+ outputLength = length;
+}
+
+void RouteShape::PrepareArrowGeometry(vector<m2::PointD> const & path, m2::RectF const & texRect, float depth,
+ TArrowGeometryBuffer & geometry, TArrowGeometryBuffer & joinsGeometry)
+{
+ ASSERT(path.size() > 1, ());
- // calculate joins bounds
- if (!isRoute)
+ // Construct segments.
+ vector<LineSegment> segments;
+ segments.reserve(path.size() - 1);
+ ConstructLineSegments(path, segments);
+
+ float finalLength = 0.0f;
+ for (size_t i = 0; i < segments.size(); i++)
+ finalLength += glsl::length(segments[i].m_points[EndPoint] - segments[i].m_points[StartPoint]);
+
+ m2::RectF tr = texRect;
+ tr.setMinX(texRect.minX() * (1.0 - kArrowTailSize) + texRect.maxX() * kArrowTailSize);
+ tr.setMaxX(texRect.minX() * kArrowHeadSize + texRect.maxX() * (1.0 - kArrowHeadSize));
+
+ // Build geometry.
+ float length = 0;
+ for (size_t i = 0; i < segments.size(); i++)
{
- float const eps = 1e-5;
- double len = 0;
- for (size_t i = 0; i < segments.size() - 1; i++)
+ UpdateNormals(&segments[i], (i > 0) ? &segments[i - 1] : nullptr,
+ (i < segments.size() - 1) ? &segments[i + 1] : nullptr);
+
+ // Generate main geometry.
+ glsl::vec4 const startPivot = glsl::vec4(segments[i].m_points[StartPoint], depth, 1.0);
+ glsl::vec4 const endPivot = glsl::vec4(segments[i].m_points[EndPoint], depth, 1.0);
+
+ float const endLength = length + glsl::length(segments[i].m_points[EndPoint] - segments[i].m_points[StartPoint]);
+
+ glsl::vec2 const leftNormalStart = GetNormal(segments[i], true /* isLeft */, StartNormal);
+ glsl::vec2 const rightNormalStart = GetNormal(segments[i], false /* isLeft */, StartNormal);
+ glsl::vec2 const leftNormalEnd = GetNormal(segments[i], true /* isLeft */, EndNormal);
+ glsl::vec2 const rightNormalEnd = GetNormal(segments[i], false /* isLeft */, EndNormal);
+
+ float const startU = length / finalLength;
+ float const endU = endLength / finalLength;
+
+ geometry.push_back(AV(startPivot, glsl::vec2(0, 0), GetUV(tr, startU, 0.5f)));
+ geometry.push_back(AV(startPivot, leftNormalStart, GetUV(tr, startU, 0.0f)));
+ geometry.push_back(AV(endPivot, glsl::vec2(0, 0), GetUV(tr, endU, 0.5f)));
+ geometry.push_back(AV(endPivot, leftNormalEnd, GetUV(tr, endU, 0.0f)));
+
+ geometry.push_back(AV(startPivot, rightNormalStart, GetUV(tr, startU, 1.0f)));
+ geometry.push_back(AV(startPivot, glsl::vec2(0, 0), GetUV(tr, startU, 0.5f)));
+ geometry.push_back(AV(endPivot, rightNormalEnd, GetUV(tr, endU, 1.0f)));
+ geometry.push_back(AV(endPivot, glsl::vec2(0, 0), GetUV(tr, endU, 0.5f)));
+
+ // Generate joins.
+ if (segments[i].m_generateJoin && i < segments.size() - 1)
{
- len += glsl::length(segments[i].m_points[EndPoint] - segments[i].m_points[StartPoint]);
+ glsl::vec2 n1 = segments[i].m_hasLeftJoin[EndPoint] ? segments[i].m_leftNormals[EndPoint] :
+ segments[i].m_rightNormals[EndPoint];
+ glsl::vec2 n2 = segments[i + 1].m_hasLeftJoin[StartPoint] ? segments[i + 1].m_leftNormals[StartPoint] :
+ segments[i + 1].m_rightNormals[StartPoint];
- RouteJoinBounds bounds;
- bounds.m_start = min(segments[i].m_leftWidthScalar[EndPoint].y,
- segments[i].m_rightWidthScalar[EndPoint].y);
- bounds.m_end = max(-segments[i + 1].m_leftWidthScalar[StartPoint].y,
- -segments[i + 1].m_rightWidthScalar[StartPoint].y);
+ float widthScalar = segments[i].m_hasLeftJoin[EndPoint] ? segments[i].m_rightWidthScalar[EndPoint].x :
+ segments[i].m_leftWidthScalar[EndPoint].x;
- if (fabs(bounds.m_end - bounds.m_start) < eps)
- continue;
+ int const kAverageSize = 24;
+ vector<glsl::vec2> normals;
+ normals.reserve(kAverageSize);
+ vector<glsl::vec2> uv;
+ uv.reserve(kAverageSize);
- bounds.m_offset = len;
- joinsBounds.push_back(bounds);
+ GenerateJoinNormals(dp::RoundJoin, n1, n2, 1.0f, segments[i].m_hasLeftJoin[EndPoint],
+ widthScalar, normals, &uv);
+
+ ASSERT_EQUAL(normals.size(), uv.size(), ());
+
+ GenerateArrowsTriangles(glsl::vec4(segments[i].m_points[EndPoint], depth, 1.0),
+ normals, tr, uv, true /* normalizedUV */, joinsGeometry);
}
+
+ // Generate arrow head.
+ if (i == segments.size() - 1)
+ {
+ vector<glsl::vec2> normals =
+ {
+ segments[i].m_rightNormals[EndPoint],
+ segments[i].m_leftNormals[EndPoint],
+ kArrowHeadFactor * segments[i].m_tangent
+ };
+ float const u = 1.0f - kArrowHeadSize;
+ vector<glsl::vec2> uv = { glsl::vec2(u, 1.0f), glsl::vec2(u, 0.0f), glsl::vec2(1.0f, 0.5f) };
+ GenerateArrowsTriangles(glsl::vec4(segments[i].m_points[EndPoint], depth, 1.0),
+ normals, texRect, uv, true /* normalizedUV */, joinsGeometry);
+ }
+
+ // Generate arrow tail.
+ if (i == 0)
+ {
+ glsl::vec2 const n1 = segments[i].m_leftNormals[StartPoint];
+ glsl::vec2 const n2 = segments[i].m_rightNormals[StartPoint];
+ glsl::vec2 const n3 = (n1 - kArrowTailFactor * segments[i].m_tangent);
+ glsl::vec2 const n4 = (n2 - kArrowTailFactor * segments[i].m_tangent);
+ vector<glsl::vec2> normals = { n2, n4, n1, n1, n4, n3 };
+
+ m2::RectF t = texRect;
+ t.setMaxX(tr.minX());
+ vector<glsl::vec2> uv =
+ {
+ glsl::ToVec2(t.RightBottom()),
+ glsl::ToVec2(t.LeftBottom()),
+ glsl::ToVec2(t.RightTop()),
+ glsl::ToVec2(t.RightTop()),
+ glsl::ToVec2(t.LeftBottom()),
+ glsl::ToVec2(t.LeftTop())
+ };
+
+ GenerateArrowsTriangles(glsl::vec4(segments[i].m_points[StartPoint], depth, 1.0),
+ normals, texRect, uv, false /* normalizedUV */, joinsGeometry);
+ }
+
+ length = endLength;
}
}
@@ -289,7 +342,7 @@ void RouteShape::CacheRouteSign(ref_ptr<dp::TextureManager> mng, RouteSignData &
m2::PointF halfSize = m2::PointF(symbol.GetPixelSize()) * 0.5f;
glsl::vec2 const pos = glsl::ToVec2(routeSignData.m_position);
- glsl::vec4 const pivot = glsl::vec4(pos.x, pos.y, m_params.m_depth, 0.0f);
+ glsl::vec4 const pivot = glsl::vec4(pos.x, pos.y, 0.0f /* depth */, 0.0f /* pivot z */);
gpu::SolidTexturingVertex data[4]=
{
{ pivot, glsl::vec2(-halfSize.x, halfSize.y), glsl::ToVec2(texRect.LeftTop()) },
@@ -320,48 +373,50 @@ void RouteShape::CacheRouteSign(ref_ptr<dp::TextureManager> mng, RouteSignData &
}
}
-void RouteShape::Draw(ref_ptr<dp::TextureManager> textures, RouteData & routeData)
+void RouteShape::CacheRouteArrows(ref_ptr<dp::TextureManager> mng, m2::PolylineD const & polyline,
+ vector<ArrowBorders> const & borders, RouteArrowsData & routeArrowsData)
{
- // route geometry
+ TArrowGeometryBuffer geometry;
+ TArrowGeometryBuffer joinsGeometry;
+ dp::TextureManager::SymbolRegion region;
+ GetArrowTextureRegion(mng, region);
+ dp::GLState state = dp::GLState(gpu::ROUTE_ARROW_PROGRAM, dp::GLState::GeometryLayer);
+ state.SetColorTexture(region.GetTexture());
+
+ // Generate arrow geometry.
+ float depth = 0.0f;
+ for (ArrowBorders const & b : borders)
{
- TGeometryBuffer geometry;
- TGeometryBuffer joinsGeometry;
- vector<RouteJoinBounds> bounds;
- PrepareGeometry(true /* isRoute */, routeData.m_sourcePolyline.GetPoints(),
- geometry, joinsGeometry, bounds, routeData.m_length);
-
- dp::GLState state = dp::GLState(gpu::ROUTE_PROGRAM, dp::GLState::GeometryLayer);
- state.SetColorTexture(textures->GetSymbolsTexture());
- BatchGeometry(state, geometry, joinsGeometry, routeData.m_route);
+ vector<m2::PointD> points = CalculatePoints(polyline, b.m_startDistance, b.m_endDistance);
+ ASSERT_LESS_OR_EQUAL(points.size(), polyline.GetSize(), ());
+ PrepareArrowGeometry(points, region.GetTexRect(), depth, geometry, joinsGeometry);
+ depth += 1.0f;
}
- // arrows geometry
- if (!routeData.m_sourceTurns.empty())
- {
- dp::TextureManager::SymbolRegion region;
- GetArrowTextureRegion(textures, region);
- routeData.m_arrowTextureRect = region.GetTexRect();
-
- dp::GLState state = dp::GLState(gpu::ROUTE_ARROW_PROGRAM, dp::GLState::GeometryLayer);
- state.SetColorTexture(region.GetTexture());
+ BatchGeometry(state, make_ref(geometry.data()), geometry.size(),
+ make_ref(joinsGeometry.data()), joinsGeometry.size(),
+ AV::GetBindingInfo(), routeArrowsData.m_arrows);
+}
- ClipArrowToSegments(routeData.m_sourceTurns, routeData);
- for (auto & renderProperty : routeData.m_arrows)
- {
- TGeometryBuffer geometry;
- TGeometryBuffer joinsGeometry;
- vector<m2::PointD> const points = CalculatePoints(routeData.m_sourcePolyline, renderProperty->m_start, renderProperty->m_end);
- ASSERT_LESS_OR_EQUAL(points.size(), routeData.m_sourcePolyline.GetSize(), ());
- PrepareGeometry(false /* isRoute */, points, geometry, joinsGeometry, renderProperty->m_joinsBounds, routeData.m_length);
- BatchGeometry(state, geometry, joinsGeometry, renderProperty->m_arrow);
- }
- }
+void RouteShape::CacheRoute(ref_ptr<dp::TextureManager> textures, RouteData & routeData)
+{
+ TGeometryBuffer geometry;
+ TGeometryBuffer joinsGeometry;
+ PrepareGeometry(routeData.m_sourcePolyline.GetPoints(),
+ geometry, joinsGeometry, routeData.m_length);
+
+ dp::GLState state = dp::GLState(gpu::ROUTE_PROGRAM, dp::GLState::GeometryLayer);
+ state.SetColorTexture(textures->GetSymbolsTexture());
+ BatchGeometry(state, make_ref(geometry.data()), geometry.size(),
+ make_ref(joinsGeometry.data()), joinsGeometry.size(),
+ RV::GetBindingInfo(), routeData.m_route);
}
-void RouteShape::BatchGeometry(dp::GLState const & state, TGeometryBuffer & geometry,
- TGeometryBuffer & joinsGeometry, RouteRenderProperty & property)
+void RouteShape::BatchGeometry(dp::GLState const & state, ref_ptr<void> geometry, size_t geomSize,
+ ref_ptr<void> joinsGeometry, size_t joinsGeomSize,
+ dp::BindingInfo const & bindingInfo, RouteRenderProperty & property)
{
- size_t const verticesCount = geometry.size() + joinsGeometry.size();
+ size_t const verticesCount = geomSize + joinsGeomSize;
if (verticesCount != 0)
{
uint32_t const kBatchSize = 5000;
@@ -372,14 +427,17 @@ void RouteShape::BatchGeometry(dp::GLState const & state, TGeometryBuffer & geom
property.m_state = state;
});
- dp::AttributeProvider provider(1 /* stream count */, geometry.size());
- provider.InitStream(0 /* stream index */, gpu::RouteVertex::GetBindingInfo(), make_ref(geometry.data()));
- batcher.InsertListOfStrip(state, make_ref(&provider), 4);
+ if (geomSize != 0)
+ {
+ dp::AttributeProvider provider(1 /* stream count */, geomSize);
+ provider.InitStream(0 /* stream index */, bindingInfo, geometry);
+ batcher.InsertListOfStrip(state, make_ref(&provider), 4);
+ }
- if (!joinsGeometry.empty())
+ if (joinsGeomSize != 0)
{
- dp::AttributeProvider joinsProvider(1 /* stream count */, joinsGeometry.size());
- joinsProvider.InitStream(0 /* stream index */, gpu::RouteVertex::GetBindingInfo(), make_ref(joinsGeometry.data()));
+ dp::AttributeProvider joinsProvider(1 /* stream count */, joinsGeomSize);
+ joinsProvider.InitStream(0 /* stream index */, bindingInfo, joinsGeometry);
batcher.InsertTriangleList(state, make_ref(&joinsProvider));
}
}
diff --git a/drape_frontend/route_shape.hpp b/drape_frontend/route_shape.hpp
index 8d0155631e..21af6439fa 100644
--- a/drape_frontend/route_shape.hpp
+++ b/drape_frontend/route_shape.hpp
@@ -18,6 +18,16 @@ namespace df
double const kArrowSize = 0.001;
+// Constants below depend on arrow texture.
+double const kArrowHeadSize = 124.0 / 400.0;
+float const kArrowHeadFactor = 124.0f / 96.0f;
+
+double const kArrowTailSize = 20.0 / 400.0;
+float const kArrowTailFactor = 20.0f / 96.0f;
+
+double const kArrowHeightFactor = 96.0 / 36.0;
+double const kArrowAspect = 400.0 / 192.0;
+
struct RoutePattern
{
bool m_isDashed = false;
@@ -33,13 +43,6 @@ struct RoutePattern
{}
};
-struct RouteJoinBounds
-{
- double m_start = 0;
- double m_end = 0;
- double m_offset = 0;
-};
-
struct RouteRenderProperty
{
dp::GLState m_state;
@@ -47,24 +50,21 @@ struct RouteRenderProperty
RouteRenderProperty() : m_state(0, dp::GLState::GeometryLayer) {}
};
-struct ArrowRenderProperty
+struct ArrowBorders
{
- vector<RouteJoinBounds> m_joinsBounds;
- vector<double> m_turns;
- double m_start;
- double m_end;
- RouteRenderProperty m_arrow;
+ double m_startDistance = 0;
+ double m_endDistance = 0;
+ int m_groupIndex = 0;
};
struct RouteData
{
+ int m_routeIndex;
m2::PolylineD m_sourcePolyline;
vector<double> m_sourceTurns;
df::ColorConstant m_color;
- m2::RectF m_arrowTextureRect;
double m_length;
RouteRenderProperty m_route;
- vector<drape_ptr<ArrowRenderProperty>> m_arrows;
RoutePattern m_pattern;
};
@@ -76,24 +76,32 @@ struct RouteSignData
m2::PointD m_position;
};
+struct RouteArrowsData
+{
+ RouteRenderProperty m_arrows;
+};
+
class RouteShape
{
public:
- RouteShape(CommonViewParams const & params);
- void CacheRouteSign(ref_ptr<dp::TextureManager> mng, RouteSignData & routeSignData);
- void Draw(ref_ptr<dp::TextureManager> textures, RouteData & routeData);
-
-private:
using RV = gpu::RouteVertex;
- using TGeometryBuffer = buffer_vector<gpu::RouteVertex, 128>;
+ using TGeometryBuffer = buffer_vector<RV, 128>;
+ using AV = gpu::SolidTexturingVertex;
+ using TArrowGeometryBuffer = buffer_vector<AV, 128>;
- void PrepareGeometry(bool isRoute, vector<m2::PointD> const & path,
- TGeometryBuffer & geometry, TGeometryBuffer & joinsGeometry,
- vector<RouteJoinBounds> & joinsBounds, double & outputLength);
- void BatchGeometry(dp::GLState const & state, TGeometryBuffer & geometry,
- TGeometryBuffer & joinsGeometry, RouteRenderProperty & property);
+ static void CacheRoute(ref_ptr<dp::TextureManager> textures, RouteData & routeData);
+ static void CacheRouteSign(ref_ptr<dp::TextureManager> mng, RouteSignData & routeSignData);
+ static void CacheRouteArrows(ref_ptr<dp::TextureManager> mng, m2::PolylineD const & polyline,
+ vector<ArrowBorders> const & borders, RouteArrowsData & routeArrowsData);
- CommonViewParams m_params;
+private:
+ static void PrepareGeometry(vector<m2::PointD> const & path, TGeometryBuffer & geometry,
+ TGeometryBuffer & joinsGeometry, double & outputLength);
+ static void PrepareArrowGeometry(vector<m2::PointD> const & path, m2::RectF const & texRect, float depth,
+ TArrowGeometryBuffer & geometry, TArrowGeometryBuffer & joinsGeometry);
+ static void BatchGeometry(dp::GLState const & state, ref_ptr<void> geometry, size_t geomSize,
+ ref_ptr<void> joinsGeometry, size_t joinsGeomSize,
+ dp::BindingInfo const & bindingInfo, RouteRenderProperty & property);
};
} // namespace df