forked from organicmaps/organicmaps
Improved route rendering in drape
This commit is contained in:
parent
7e05661e00
commit
f7908dd5e4
10 changed files with 478 additions and 334 deletions
|
@ -34,12 +34,10 @@ BackendRenderer::BackendRenderer(Params const & params)
|
|||
MessagePriority::High);
|
||||
});
|
||||
|
||||
m_routeBuilder = make_unique_dp<RouteBuilder>([this](dp::GLState const & state, drape_ptr<dp::RenderBucket> && bucket,
|
||||
RouteData const & routeData, dp::GLState const & endOfRouteState,
|
||||
drape_ptr<dp::RenderBucket> && endOfRouteBucket)
|
||||
m_routeBuilder = make_unique_dp<RouteBuilder>([this](drape_ptr<RouteData> && routeData)
|
||||
{
|
||||
m_commutator->PostMessage(ThreadsCommutator::RenderThread,
|
||||
make_unique_dp<FlushRouteMessage>(state, move(bucket), routeData, endOfRouteState, move(endOfRouteBucket)),
|
||||
make_unique_dp<FlushRouteMessage>(move(routeData)),
|
||||
MessagePriority::Normal);
|
||||
});
|
||||
|
||||
|
|
|
@ -302,16 +302,8 @@ void FrontendRenderer::AcceptMessage(ref_ptr<Message> message)
|
|||
case Message::FlushRoute:
|
||||
{
|
||||
ref_ptr<FlushRouteMessage> msg = message;
|
||||
dp::GLState const & state = msg->GetState();
|
||||
drape_ptr<dp::RenderBucket> bucket = msg->AcceptBuffer();
|
||||
m_routeRenderer->AddRouteRenderBucket(state, move(bucket), msg->GetRouteData(), make_ref(m_gpuProgramManager));
|
||||
|
||||
dp::GLState const & eorState = msg->GetEndOfRouteState();
|
||||
drape_ptr<dp::RenderBucket> eorBucket = msg->AcceptEndOfRouteBuffer();
|
||||
if (eorBucket != nullptr)
|
||||
{
|
||||
m_routeRenderer->AddEndOfRouteRenderBucket(eorState, move(eorBucket), make_ref(m_gpuProgramManager));
|
||||
}
|
||||
drape_ptr<RouteData> routeData = msg->AcceptRouteData();
|
||||
m_routeRenderer->SetRouteData(move(routeData), make_ref(m_gpuProgramManager));
|
||||
|
||||
m_myPositionController->ActivateRouting();
|
||||
break;
|
||||
|
|
|
@ -533,31 +533,15 @@ private:
|
|||
class FlushRouteMessage : public Message
|
||||
{
|
||||
public:
|
||||
FlushRouteMessage(dp::GLState const & state, drape_ptr<dp::RenderBucket> && buffer,
|
||||
RouteData const & routeData, dp::GLState const & endOfRouteState,
|
||||
drape_ptr<dp::RenderBucket> && endOfRouteBuffer)
|
||||
: m_state(state)
|
||||
, m_buffer(move(buffer))
|
||||
, m_routeData(routeData)
|
||||
, m_endOfRouteState(endOfRouteState)
|
||||
, m_endOfRouteBuffer(move(endOfRouteBuffer))
|
||||
FlushRouteMessage(drape_ptr<RouteData> && routeData)
|
||||
: m_routeData(move(routeData))
|
||||
{}
|
||||
|
||||
Type GetType() const override { return Message::FlushRoute; }
|
||||
|
||||
dp::GLState const & GetState() const { return m_state; }
|
||||
drape_ptr<dp::RenderBucket> && AcceptBuffer() { return move(m_buffer); }
|
||||
RouteData const & GetRouteData() const { return m_routeData; }
|
||||
|
||||
dp::GLState const & GetEndOfRouteState() const { return m_endOfRouteState; }
|
||||
drape_ptr<dp::RenderBucket> && AcceptEndOfRouteBuffer() { return move(m_endOfRouteBuffer); }
|
||||
drape_ptr<RouteData> && AcceptRouteData() { return move(m_routeData); }
|
||||
|
||||
private:
|
||||
dp::GLState m_state;
|
||||
drape_ptr<dp::RenderBucket> m_buffer;
|
||||
RouteData m_routeData;
|
||||
dp::GLState m_endOfRouteState;
|
||||
drape_ptr<dp::RenderBucket> m_endOfRouteBuffer;
|
||||
drape_ptr<RouteData> m_routeData;
|
||||
};
|
||||
|
||||
class UpdateMapStyleMessage : public Message
|
||||
|
|
|
@ -5,11 +5,8 @@
|
|||
namespace df
|
||||
{
|
||||
|
||||
const int ESTIMATE_BUFFER_SIZE = 4000;
|
||||
|
||||
RouteBuilder::RouteBuilder(RouteBuilder::TFlushRouteFn const & flushRouteFn)
|
||||
: m_flushRouteFn(flushRouteFn)
|
||||
, m_batcher(make_unique_dp<dp::Batcher>(ESTIMATE_BUFFER_SIZE, ESTIMATE_BUFFER_SIZE))
|
||||
{}
|
||||
|
||||
void RouteBuilder::Build(m2::PolylineD const & routePolyline, vector<double> const & turns,
|
||||
|
@ -18,29 +15,12 @@ void RouteBuilder::Build(m2::PolylineD const & routePolyline, vector<double> co
|
|||
CommonViewParams params;
|
||||
params.m_depth = 0.0f;
|
||||
|
||||
RouteShape shape(routePolyline, params);
|
||||
m2::RectF textureRect = shape.GetArrowTextureRect(textures);
|
||||
shape.PrepareGeometry(textures);
|
||||
drape_ptr<RouteData> routeData = make_unique_dp<RouteData>();
|
||||
routeData->m_color = color;
|
||||
RouteShape(routePolyline, turns, params).Draw(textures, *routeData.get());
|
||||
|
||||
RouteData routeData;
|
||||
routeData.m_color = color;
|
||||
routeData.m_arrowTextureRect = textureRect;
|
||||
routeData.m_joinsBounds = shape.GetJoinsBounds();
|
||||
routeData.m_length = shape.GetLength();
|
||||
routeData.m_turns = turns;
|
||||
|
||||
dp::GLState eorState = shape.GetEndOfRouteState();
|
||||
drape_ptr<dp::RenderBucket> eorBucket = shape.MoveEndOfRouteRenderBucket();
|
||||
|
||||
auto flushRoute = [this, &routeData, &eorState, &eorBucket](dp::GLState const & state,
|
||||
drape_ptr<dp::RenderBucket> && bucket)
|
||||
{
|
||||
if (m_flushRouteFn != nullptr)
|
||||
m_flushRouteFn(state, move(bucket), routeData, eorState, move(eorBucket));
|
||||
};
|
||||
|
||||
dp::SessionGuard guard(*m_batcher, flushRoute);
|
||||
shape.Draw(make_ref(m_batcher), textures);
|
||||
if (m_flushRouteFn != nullptr)
|
||||
m_flushRouteFn(move(routeData));
|
||||
}
|
||||
|
||||
} // namespace df
|
||||
|
|
|
@ -2,34 +2,20 @@
|
|||
|
||||
#include "drape_frontend/route_shape.hpp"
|
||||
|
||||
#include "drape/batcher.hpp"
|
||||
#include "drape/glstate.hpp"
|
||||
#include "drape/pointers.hpp"
|
||||
#include "drape/render_bucket.hpp"
|
||||
#include "drape/texture_manager.hpp"
|
||||
|
||||
#include "geometry/polyline2d.hpp"
|
||||
|
||||
#include "std/function.hpp"
|
||||
#include "std/vector.hpp"
|
||||
|
||||
namespace df
|
||||
{
|
||||
|
||||
struct RouteData
|
||||
{
|
||||
dp::Color m_color;
|
||||
m2::RectF m_arrowTextureRect;
|
||||
vector<RouteJoinBounds> m_joinsBounds;
|
||||
double m_length;
|
||||
vector<double> m_turns;
|
||||
};
|
||||
|
||||
class RouteBuilder
|
||||
{
|
||||
public:
|
||||
using TFlushRouteFn = function<void(dp::GLState const &, drape_ptr<dp::RenderBucket> &&, RouteData const &,
|
||||
dp::GLState const &, drape_ptr<dp::RenderBucket> &&)>;
|
||||
using TFlushRouteFn = function<void(drape_ptr<RouteData> &&)>;
|
||||
|
||||
RouteBuilder(TFlushRouteFn const & flushRouteFn);
|
||||
|
||||
|
@ -38,7 +24,6 @@ public:
|
|||
|
||||
private:
|
||||
TFlushRouteFn m_flushRouteFn;
|
||||
drape_ptr<dp::Batcher> m_batcher;
|
||||
};
|
||||
|
||||
} // namespace df
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "drape/glsl_func.hpp"
|
||||
#include "drape/shader_def.hpp"
|
||||
#include "drape/utils/projection.hpp"
|
||||
#include "drape/vertex_array_buffer.hpp"
|
||||
|
||||
#include "indexer/scales.hpp"
|
||||
|
||||
|
@ -14,22 +15,38 @@ namespace df
|
|||
namespace
|
||||
{
|
||||
|
||||
float const halfWidthInPixel[] =
|
||||
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
|
||||
1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 2.0f, 2.0f, 2.0f, 2.0f,
|
||||
//11 12 13 14 15 16 17 18 19
|
||||
2.0f, 2.5f, 3.5f, 5.0f, 7.5f, 10.0f, 14.0f, 18.0f, 36.0f,
|
||||
2.0f, 2.0f, 3.0f, 3.0f, 3.0f, 4.0f, 4.0f, 4.0f, 5.0f, 5.0f,
|
||||
//11 12 13 14 15 16 17 18 19 20
|
||||
6.0f, 6.0f, 7.0f, 7.0f, 7.0f, 7.0f, 8.0f, 10.0f, 24.0f, 36.0f
|
||||
};
|
||||
|
||||
int const arrowAppearingZoomLevel = 14;
|
||||
uint8_t const kAlphaValue[] =
|
||||
{
|
||||
//1 2 3 4 5 6 7 8 9 10
|
||||
204, 204, 204, 204, 204, 204, 204, 204, 204, 204,
|
||||
//11 12 13 14 15 16 17 18 19 20
|
||||
204, 204, 204, 204, 190, 180, 170, 160, 140, 120
|
||||
};
|
||||
|
||||
int const arrowPartsCount = 3;
|
||||
double const arrowHeightFactor = 96.0 / 36.0;
|
||||
double const arrowAspect = 400.0 / 192.0;
|
||||
double const arrowTailSize = 20.0 / 400.0;
|
||||
double const arrowHeadSize = 124.0 / 400.0;
|
||||
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++)
|
||||
|
@ -37,43 +54,52 @@ int CheckForIntersection(double start, double end, vector<RouteSegment> const &
|
|||
if (segments[i].m_isAvailable)
|
||||
continue;
|
||||
|
||||
if ((start >= segments[i].m_start && start <= segments[i].m_end) ||
|
||||
(end >= segments[i].m_start && end <= segments[i].m_end) ||
|
||||
(start < segments[i].m_start && end > segments[i].m_end))
|
||||
if (start <= segments[i].m_end && end >= segments[i].m_start)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
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 threshold = 0.8;
|
||||
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
|
||||
if (index != -1)
|
||||
double const len = end - start;
|
||||
for (size_t i = index; i < segments.size(); i++)
|
||||
{
|
||||
double const len = end - start;
|
||||
for (int i = index; i < (int)segments.size(); i++)
|
||||
{
|
||||
double const factor = (segments[i].m_end - segments[i].m_start) / len;
|
||||
if (segments[i].m_isAvailable && factor > threshold)
|
||||
return (int)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;
|
||||
}
|
||||
|
||||
return -1;
|
||||
void ClipBorders(vector<ArrowBorders> & borders)
|
||||
{
|
||||
auto invalidBorders = [](ArrowBorders const & borders)
|
||||
{
|
||||
return borders.m_groupIndex == kInvalidGroup;
|
||||
};
|
||||
borders.erase(remove_if(borders.begin(), borders.end(), invalidBorders), borders.end());
|
||||
}
|
||||
|
||||
void MergeAndClipBorders(vector<ArrowBorders> & borders)
|
||||
{
|
||||
// initial clipping
|
||||
ClipBorders(borders);
|
||||
|
||||
if (borders.empty())
|
||||
return;
|
||||
|
||||
// mark groups
|
||||
for (size_t i = 0; i < borders.size() - 1; i++)
|
||||
for (size_t i = 0; i + 1 < borders.size(); i++)
|
||||
{
|
||||
if (borders[i].m_endDistance >= borders[i + 1].m_startDistance)
|
||||
borders[i + 1].m_groupIndex = borders[i].m_groupIndex;
|
||||
|
@ -92,57 +118,69 @@ void MergeAndClipBorders(vector<ArrowBorders> & borders)
|
|||
}
|
||||
else
|
||||
{
|
||||
borders[i].m_groupIndex = -1;
|
||||
borders[i].m_groupIndex = kInvalidGroup;
|
||||
}
|
||||
}
|
||||
borders[lastGroupIndex].m_endDistance = borders.back().m_endDistance;
|
||||
|
||||
// clip groups
|
||||
auto const iter = remove_if(borders.begin(), borders.end(), [](ArrowBorders const & borders)
|
||||
{
|
||||
return borders.m_groupIndex == -1;
|
||||
});
|
||||
borders.erase(iter, borders.end());
|
||||
ClipBorders(borders);
|
||||
}
|
||||
|
||||
void BuildBuckets(RouteRenderProperty const & renderProperty, ref_ptr<dp::GpuProgramManager> mng)
|
||||
{
|
||||
for (drape_ptr<dp::RenderBucket> const & bucket : renderProperty.m_buckets)
|
||||
bucket->GetBuffer()->Build(mng->GetProgram(renderProperty.m_state.GetProgramIndex()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RouteGraphics::RouteGraphics(dp::GLState const & state,
|
||||
drape_ptr<dp::VertexArrayBuffer> && buffer,
|
||||
dp::Color const & color)
|
||||
: m_state(state)
|
||||
, m_buffer(move(buffer))
|
||||
, m_color(color)
|
||||
{}
|
||||
|
||||
RouteRenderer::RouteRenderer()
|
||||
: m_distanceFromBegin(0.0)
|
||||
, m_endOfRouteState(0, dp::GLState::OverlayLayer)
|
||||
{}
|
||||
|
||||
void RouteRenderer::InterpolateByZoom(ScreenBase const & screen, float & halfWidth, float & alpha, double & zoom) const
|
||||
{
|
||||
double const zoomLevel = my::clamp(fabs(log(screen.GetScale()) / log(2.0)), 1.0, scales::UPPER_STYLE_SCALE + 1.0);
|
||||
zoom = trunc(zoomLevel);
|
||||
int const index = zoom - 1.0;
|
||||
float const lerpCoef = zoomLevel - zoom;
|
||||
|
||||
if (index < scales::UPPER_STYLE_SCALE)
|
||||
{
|
||||
halfWidth = kHalfWidthInPixel[index] + lerpCoef * (kHalfWidthInPixel[index + 1] - kHalfWidthInPixel[index]);
|
||||
|
||||
float const alpha1 = static_cast<float>(kAlphaValue[index]) / numeric_limits<uint8_t>::max();
|
||||
float const alpha2 = static_cast<float>(kAlphaValue[index + 1]) / numeric_limits<uint8_t>::max();
|
||||
alpha = alpha1 + lerpCoef * (alpha2 - alpha1);
|
||||
}
|
||||
else
|
||||
{
|
||||
halfWidth = kHalfWidthInPixel[scales::UPPER_STYLE_SCALE];
|
||||
alpha = static_cast<float>(kAlphaValue[scales::UPPER_STYLE_SCALE]) / numeric_limits<uint8_t>::max();
|
||||
}
|
||||
}
|
||||
|
||||
void RouteRenderer::Render(ScreenBase const & screen, ref_ptr<dp::GpuProgramManager> mng,
|
||||
dp::UniformValuesStorage const & commonUniforms)
|
||||
{
|
||||
// half width calculation
|
||||
if (!m_routeData)
|
||||
return;
|
||||
|
||||
// interpolate values by zoom level
|
||||
double zoom = 0.0;
|
||||
float halfWidth = 0.0;
|
||||
double const zoomLevel = my::clamp(fabs(log(screen.GetScale()) / log(2.0)), 1.0, scales::UPPER_STYLE_SCALE);
|
||||
double const truncedZoom = trunc(zoomLevel);
|
||||
int const index = truncedZoom - 1.0;
|
||||
float const lerpCoef = zoomLevel - truncedZoom;
|
||||
float alpha = 0.0;
|
||||
InterpolateByZoom(screen, halfWidth, alpha, zoom);
|
||||
|
||||
if (index < scales::UPPER_STYLE_SCALE - 1)
|
||||
halfWidth = halfWidthInPixel[index] + lerpCoef * (halfWidthInPixel[index + 1] - halfWidthInPixel[index]);
|
||||
else
|
||||
halfWidth = halfWidthInPixel[index];
|
||||
|
||||
if (!m_routeGraphics.empty())
|
||||
// render route
|
||||
{
|
||||
dp::GLState const & state = m_routeGraphics.front().m_state;
|
||||
dp::GLState const & state = m_routeData->m_route.m_state;
|
||||
|
||||
// set up uniforms
|
||||
dp::UniformValuesStorage uniformStorage;
|
||||
glsl::vec4 color = glsl::ToVec4(m_routeGraphics.front().m_color);
|
||||
uniformStorage.SetFloatValue("u_color", color.r, color.g, color.b, color.a);
|
||||
glsl::vec4 color = glsl::ToVec4(m_routeData->m_color);
|
||||
uniformStorage.SetFloatValue("u_color", color.r, color.g, color.b, alpha);
|
||||
uniformStorage.SetFloatValue("u_halfWidth", halfWidth, halfWidth * screen.GetScale());
|
||||
uniformStorage.SetFloatValue("u_clipLength", m_distanceFromBegin);
|
||||
|
||||
|
@ -154,57 +192,58 @@ void RouteRenderer::Render(ScreenBase const & screen, ref_ptr<dp::GpuProgramMana
|
|||
dp::ApplyUniforms(uniformStorage, prg);
|
||||
|
||||
// render routes
|
||||
for (RouteGraphics & graphics : m_routeGraphics)
|
||||
{
|
||||
ASSERT(graphics.m_state == state, ());
|
||||
graphics.m_buffer->Render();
|
||||
}
|
||||
for (drape_ptr<dp::RenderBucket> const & bucket : m_routeData->m_route.m_buckets)
|
||||
bucket->Render(screen);
|
||||
}
|
||||
|
||||
// render arrows
|
||||
if (truncedZoom >= arrowAppearingZoomLevel)
|
||||
{
|
||||
// set up shaders and apply common uniforms
|
||||
ref_ptr<dp::GpuProgram> prgArrow = mng->GetProgram(gpu::ROUTE_ARROW_PROGRAM);
|
||||
prgArrow->Bind();
|
||||
dp::ApplyState(state, prgArrow);
|
||||
dp::ApplyUniforms(commonUniforms, prgArrow);
|
||||
// render arrows
|
||||
if (zoom >= kArrowAppearingZoomLevel && !m_routeData->m_arrows.empty())
|
||||
{
|
||||
dp::GLState const & state = m_routeData->m_arrows.front()->m_arrow.m_state;
|
||||
|
||||
for (RouteGraphics & graphics : m_routeGraphics)
|
||||
RenderArrow(prgArrow, graphics, halfWidth, screen);
|
||||
}
|
||||
// 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());
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// render end of route
|
||||
if (m_endOfRouteBuffer != nullptr)
|
||||
{
|
||||
dp::GLState const & state = m_routeData->m_endOfRouteSign.m_state;
|
||||
|
||||
dp::UniformValuesStorage uniforms = commonUniforms;
|
||||
uniforms.SetFloatValue("u_opacity", 1.0);
|
||||
ref_ptr<dp::GpuProgram> eorProgram = mng->GetProgram(m_endOfRouteState.GetProgramIndex());
|
||||
eorProgram->Bind();
|
||||
dp::ApplyState(m_endOfRouteState, eorProgram);
|
||||
dp::ApplyUniforms(uniforms, eorProgram);
|
||||
m_endOfRouteBuffer->Render();
|
||||
ref_ptr<dp::GpuProgram> program = mng->GetProgram(state.GetProgramIndex());
|
||||
program->Bind();
|
||||
dp::ApplyState(m_routeData->m_endOfRouteSign.m_state, program);
|
||||
dp::ApplyUniforms(uniforms, program);
|
||||
for (drape_ptr<dp::RenderBucket> const & bucket : m_routeData->m_endOfRouteSign.m_buckets)
|
||||
bucket->Render(screen);
|
||||
}
|
||||
}
|
||||
|
||||
void RouteRenderer::RenderArrow(ref_ptr<dp::GpuProgram> prg, RouteGraphics const & graphics,
|
||||
void RouteRenderer::RenderArrow(ref_ptr<dp::GpuProgram> prg, drape_ptr<ArrowRenderProperty> const & property,
|
||||
float halfWidth, ScreenBase const & screen)
|
||||
{
|
||||
double const arrowHalfWidth = halfWidth * arrowHeightFactor;
|
||||
double const arrowHalfWidth = halfWidth * kArrowHeightFactor;
|
||||
double const glbArrowHalfWidth = arrowHalfWidth * screen.GetScale();
|
||||
double const arrowSize = 0.001;
|
||||
double const textureWidth = 2.0 * arrowHalfWidth * arrowAspect;
|
||||
double const textureWidth = 2.0 * arrowHalfWidth * kArrowAspect;
|
||||
|
||||
dp::UniformValuesStorage uniformStorage;
|
||||
uniformStorage.SetFloatValue("u_halfWidth", arrowHalfWidth, glbArrowHalfWidth);
|
||||
uniformStorage.SetFloatValue("u_textureRect", m_routeData.m_arrowTextureRect.minX(),
|
||||
m_routeData.m_arrowTextureRect.minY(),
|
||||
m_routeData.m_arrowTextureRect.maxX(),
|
||||
m_routeData.m_arrowTextureRect.maxY());
|
||||
|
||||
// calculate arrows
|
||||
m_arrowBorders.clear();
|
||||
CalculateArrowBorders(arrowSize, screen.GetScale(), textureWidth, glbArrowHalfWidth);
|
||||
CalculateArrowBorders(property, kArrowSize, screen.GetScale(), textureWidth, glbArrowHalfWidth);
|
||||
|
||||
// split arrow's data by 16-elements buckets
|
||||
array<float, 16> borders;
|
||||
|
@ -227,39 +266,30 @@ void RouteRenderer::RenderArrow(ref_ptr<dp::GpuProgram> prg, RouteGraphics const
|
|||
borders.fill(0.0f);
|
||||
|
||||
dp::ApplyUniforms(uniformStorage, prg);
|
||||
graphics.m_buffer->Render();
|
||||
for (drape_ptr<dp::RenderBucket> const & bucket : property->m_arrow.m_buckets)
|
||||
bucket->Render(screen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RouteRenderer::AddRouteRenderBucket(dp::GLState const & state, drape_ptr<dp::RenderBucket> && bucket,
|
||||
RouteData const & routeData, ref_ptr<dp::GpuProgramManager> mng)
|
||||
void RouteRenderer::SetRouteData(drape_ptr<RouteData> && routeData, ref_ptr<dp::GpuProgramManager> mng)
|
||||
{
|
||||
m_routeData = routeData;
|
||||
m_routeData = move(routeData);
|
||||
|
||||
m_routeGraphics.push_back(RouteGraphics());
|
||||
RouteGraphics & route = m_routeGraphics.back();
|
||||
BuildBuckets(m_routeData->m_route, mng);
|
||||
BuildBuckets(m_routeData->m_endOfRouteSign, mng);
|
||||
for (drape_ptr<ArrowRenderProperty> const & arrow : m_routeData->m_arrows)
|
||||
BuildBuckets(arrow->m_arrow, mng);
|
||||
|
||||
route.m_state = state;
|
||||
route.m_color = m_routeData.m_color;
|
||||
route.m_buffer = bucket->MoveBuffer();
|
||||
route.m_buffer->Build(mng->GetProgram(route.m_state.GetProgramIndex()));
|
||||
}
|
||||
|
||||
void RouteRenderer::AddEndOfRouteRenderBucket(dp::GLState const & state, drape_ptr<dp::RenderBucket> && bucket,
|
||||
ref_ptr<dp::GpuProgramManager> mng)
|
||||
{
|
||||
m_endOfRouteState = state;
|
||||
m_endOfRouteBuffer = bucket->MoveBuffer();
|
||||
m_endOfRouteBuffer->Build(mng->GetProgram(m_endOfRouteState.GetProgramIndex()));
|
||||
m_distanceFromBegin = 0.0;
|
||||
}
|
||||
|
||||
void RouteRenderer::Clear()
|
||||
{
|
||||
m_routeGraphics.clear();
|
||||
m_endOfRouteBuffer.reset();
|
||||
m_routeData.reset();
|
||||
m_arrowBorders.clear();
|
||||
m_routeSegments.clear();
|
||||
m_distanceFromBegin = 0.0;
|
||||
}
|
||||
|
||||
void RouteRenderer::UpdateDistanceFromBegin(double distanceFromBegin)
|
||||
|
@ -267,68 +297,86 @@ void RouteRenderer::UpdateDistanceFromBegin(double distanceFromBegin)
|
|||
m_distanceFromBegin = distanceFromBegin;
|
||||
}
|
||||
|
||||
void RouteRenderer::ApplyJoinsBounds(double joinsBoundsScalar, double glbHeadLength)
|
||||
void RouteRenderer::ApplyJoinsBounds(drape_ptr<ArrowRenderProperty> const & property, double joinsBoundsScalar,
|
||||
double glbHeadLength, vector<ArrowBorders> & arrowBorders)
|
||||
{
|
||||
m_routeSegments.clear();
|
||||
m_routeSegments.reserve(2 * m_routeData.m_joinsBounds.size() + 1);
|
||||
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 < m_routeData.m_joinsBounds.size(); i++)
|
||||
for (size_t i = 0; i < property->m_joinsBounds.size(); i++)
|
||||
{
|
||||
double const start = m_routeData.m_joinsBounds[i].m_offset +
|
||||
m_routeData.m_joinsBounds[i].m_start * joinsBoundsScalar;
|
||||
double const end = m_routeData.m_joinsBounds[i].m_offset +
|
||||
m_routeData.m_joinsBounds[i].m_end * joinsBoundsScalar;
|
||||
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 = m_routeData.m_length;
|
||||
m_routeSegments.back().m_end = length;
|
||||
|
||||
// shift head of arrow if necessary
|
||||
bool needMerge = false;
|
||||
for (size_t i = 0; i < m_arrowBorders.size(); i++)
|
||||
for (size_t i = 0; i < arrowBorders.size(); i++)
|
||||
{
|
||||
int headIndex = FindNearestAvailableSegment(m_arrowBorders[i].m_endDistance - glbHeadLength,
|
||||
m_arrowBorders[i].m_endDistance, m_routeSegments);
|
||||
if (headIndex != -1)
|
||||
int headIndex = FindNearestAvailableSegment(arrowBorders[i].m_endDistance - glbHeadLength,
|
||||
arrowBorders[i].m_endDistance, m_routeSegments);
|
||||
if (headIndex != SegmentStatus::OK)
|
||||
{
|
||||
m_arrowBorders[i].m_endDistance = min(m_routeData.m_length, m_routeSegments[headIndex].m_start + glbHeadLength);
|
||||
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(m_arrowBorders);
|
||||
MergeAndClipBorders(arrowBorders);
|
||||
}
|
||||
|
||||
void RouteRenderer::CalculateArrowBorders(double arrowLength, double scale, double arrowTextureWidth, double joinsBoundsScalar)
|
||||
void RouteRenderer::CalculateArrowBorders(drape_ptr<ArrowRenderProperty> const & property, double arrowLength,
|
||||
double scale, double arrowTextureWidth, double joinsBoundsScalar)
|
||||
{
|
||||
if (m_routeData.m_turns.empty())
|
||||
return;
|
||||
ASSERT(!property->m_turns.empty(), ());
|
||||
|
||||
double halfLen = 0.5 * arrowLength;
|
||||
double const glbTextureWidth = arrowTextureWidth * scale;
|
||||
double const glbTailLength = arrowTailSize * glbTextureWidth;
|
||||
double const glbHeadLength = arrowHeadSize * glbTextureWidth;
|
||||
double const glbTailLength = kArrowTailSize * glbTextureWidth;
|
||||
double const glbHeadLength = kArrowHeadSize * glbTextureWidth;
|
||||
|
||||
m_arrowBorders.reserve(m_routeData.m_turns.size() * arrowPartsCount);
|
||||
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 < m_routeData.m_turns.size(); i++)
|
||||
for (size_t i = 0; i < property->m_turns.size(); i++)
|
||||
{
|
||||
ArrowBorders arrowBorders;
|
||||
arrowBorders.m_groupIndex = (int)i;
|
||||
arrowBorders.m_startDistance = max(0.0, m_routeData.m_turns[i] - halfLen * 0.8);
|
||||
arrowBorders.m_endDistance = min(m_routeData.m_length, m_routeData.m_turns[i] + halfLen * 1.2);
|
||||
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 < m_distanceFromBegin)
|
||||
continue;
|
||||
|
@ -340,7 +388,7 @@ void RouteRenderer::CalculateArrowBorders(double arrowLength, double scale, doub
|
|||
MergeAndClipBorders(m_arrowBorders);
|
||||
|
||||
// apply joins bounds to prevent draw arrow's head on a join
|
||||
ApplyJoinsBounds(joinsBoundsScalar, glbHeadLength);
|
||||
ApplyJoinsBounds(property, joinsBoundsScalar, glbHeadLength, m_arrowBorders);
|
||||
|
||||
// divide to parts of arrow
|
||||
size_t const bordersSize = m_arrowBorders.size();
|
||||
|
@ -351,12 +399,12 @@ void RouteRenderer::CalculateArrowBorders(double arrowLength, double scale, doub
|
|||
|
||||
m_arrowBorders[i].m_endDistance = startDistance + glbTailLength;
|
||||
m_arrowBorders[i].m_startTexCoord = 0.0;
|
||||
m_arrowBorders[i].m_endTexCoord = arrowTailSize;
|
||||
m_arrowBorders[i].m_endTexCoord = kArrowTailSize;
|
||||
|
||||
ArrowBorders arrowHead;
|
||||
arrowHead.m_startDistance = endDistance - glbHeadLength;
|
||||
arrowHead.m_endDistance = endDistance;
|
||||
arrowHead.m_startTexCoord = 1.0 - arrowHeadSize;
|
||||
arrowHead.m_startTexCoord = 1.0 - kArrowHeadSize;
|
||||
arrowHead.m_endTexCoord = 1.0;
|
||||
m_arrowBorders.push_back(arrowHead);
|
||||
|
||||
|
|
|
@ -2,29 +2,14 @@
|
|||
|
||||
#include "drape_frontend/route_builder.hpp"
|
||||
|
||||
#include "drape/batcher.hpp"
|
||||
#include "drape/glsl_types.hpp"
|
||||
#include "drape/glstate.hpp"
|
||||
#include "drape/gpu_program_manager.hpp"
|
||||
#include "drape/pointers.hpp"
|
||||
#include "drape/vertex_array_buffer.hpp"
|
||||
|
||||
#include "platform/location.hpp"
|
||||
#include "geometry/screenbase.hpp"
|
||||
|
||||
namespace df
|
||||
{
|
||||
|
||||
struct RouteGraphics
|
||||
{
|
||||
RouteGraphics() : m_state(0, dp::GLState::GeometryLayer) {}
|
||||
RouteGraphics(dp::GLState const & state, drape_ptr<dp::VertexArrayBuffer> && buffer,
|
||||
dp::Color const & color);
|
||||
|
||||
dp::GLState m_state;
|
||||
drape_ptr<dp::VertexArrayBuffer> m_buffer;
|
||||
dp::Color m_color;
|
||||
};
|
||||
|
||||
struct ArrowBorders
|
||||
{
|
||||
double m_startDistance = 0;
|
||||
|
@ -55,29 +40,23 @@ public:
|
|||
void Render(ScreenBase const & screen, ref_ptr<dp::GpuProgramManager> mng,
|
||||
dp::UniformValuesStorage const & commonUniforms);
|
||||
|
||||
void AddRouteRenderBucket(dp::GLState const & state, drape_ptr<dp::RenderBucket> && bucket,
|
||||
RouteData const & routeData, ref_ptr<dp::GpuProgramManager> mng);
|
||||
|
||||
void AddEndOfRouteRenderBucket(dp::GLState const & state, drape_ptr<dp::RenderBucket> && bucket,
|
||||
ref_ptr<dp::GpuProgramManager> mng);
|
||||
void SetRouteData(drape_ptr<RouteData> && routeData, ref_ptr<dp::GpuProgramManager> mng);
|
||||
|
||||
void Clear();
|
||||
|
||||
void UpdateDistanceFromBegin(double distanceFromBegin);
|
||||
|
||||
private:
|
||||
void CalculateArrowBorders(double arrowLength, double scale, double arrowTextureWidth, double joinsBoundsScalar);
|
||||
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 ApplyJoinsBounds(double joinsBoundsScalar, double glbHeadLength);
|
||||
|
||||
void RenderArrow(ref_ptr<dp::GpuProgram> prg, RouteGraphics const & graphics, float halfWidth, ScreenBase const & screen);
|
||||
|
||||
vector<RouteGraphics> m_routeGraphics;
|
||||
double m_distanceFromBegin;
|
||||
RouteData m_routeData;
|
||||
|
||||
dp::GLState m_endOfRouteState;
|
||||
drape_ptr<dp::VertexArrayBuffer> m_endOfRouteBuffer;
|
||||
drape_ptr<RouteData> m_routeData;
|
||||
|
||||
vector<ArrowBorders> m_arrowBorders;
|
||||
vector<RouteSegment> m_routeSegments;
|
||||
|
|
|
@ -16,50 +16,159 @@ namespace df
|
|||
|
||||
namespace
|
||||
{
|
||||
float const LEFT_SIDE = 1.0;
|
||||
float const CENTER = 0.0;
|
||||
float const RIGHT_SIDE = -1.0;
|
||||
|
||||
void GetArrowTextureRegion(ref_ptr<dp::TextureManager> textures, dp::TextureManager::SymbolRegion & region)
|
||||
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)
|
||||
{
|
||||
textures->GetSymbolRegion("route-arrow", region);
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
RouteShape::RouteShape(m2::PolylineD const & polyline, CommonViewParams const & params)
|
||||
: m_length(0)
|
||||
, m_params(params)
|
||||
, m_polyline(polyline)
|
||||
, m_endOfRouteState(0, dp::GLState::OverlayLayer)
|
||||
{}
|
||||
|
||||
m2::RectF RouteShape::GetArrowTextureRect(ref_ptr<dp::TextureManager> textures) const
|
||||
vector<m2::PointD> CalculatePoints(m2::PolylineD const & polyline, double start, double end)
|
||||
{
|
||||
dp::TextureManager::SymbolRegion region;
|
||||
GetArrowTextureRegion(textures, region);
|
||||
return region.GetTexRect();
|
||||
vector<m2::PointD> result;
|
||||
result.reserve(polyline.GetSize() / 4);
|
||||
|
||||
auto addIfNotExist = [&result](m2::PointD const & pnt)
|
||||
{
|
||||
if (result.empty() || result.back() != pnt)
|
||||
result.push_back(pnt);
|
||||
};
|
||||
|
||||
vector<m2::PointD> const & path = polyline.GetPoints();
|
||||
double len = 0;
|
||||
bool started = false;
|
||||
for (size_t i = 0; i + 1 < path.size(); i++)
|
||||
{
|
||||
double dist = (path[i + 1] - path[i]).Length();
|
||||
if (fabs(dist) < 1e-5)
|
||||
continue;
|
||||
|
||||
double l = len + dist;
|
||||
if (!started && start >= len && start <= l)
|
||||
{
|
||||
double k = (start - len) / dist;
|
||||
addIfNotExist(path[i] + (path[i + 1] - path[i]) * k);
|
||||
started = true;
|
||||
}
|
||||
if (!started)
|
||||
{
|
||||
len = l;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (end >= len && end <= l)
|
||||
{
|
||||
double k = (end - len) / dist;
|
||||
addIfNotExist(path[i] + (path[i + 1] - path[i]) * k);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
addIfNotExist(path[i + 1]);
|
||||
}
|
||||
len = l;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void RouteShape::PrepareGeometry(ref_ptr<dp::TextureManager> textures)
|
||||
}
|
||||
|
||||
RouteShape::RouteShape(m2::PolylineD const & polyline, vector<double> const & turns,
|
||||
CommonViewParams const & params)
|
||||
: m_params(params)
|
||||
, m_polyline(polyline)
|
||||
, m_turns(turns)
|
||||
{}
|
||||
|
||||
void RouteShape::PrepareGeometry(bool isRoute, vector<m2::PointD> const & path,
|
||||
TGeometryBuffer & geometry, TGeometryBuffer & joinsGeometry,
|
||||
vector<RouteJoinBounds> & joinsBounds, double & outputLength)
|
||||
{
|
||||
vector<m2::PointD> const & path = m_polyline.GetPoints();
|
||||
ASSERT(path.size() > 1, ());
|
||||
|
||||
auto const generateTriangles = [&](glsl::vec3 const & pivot, vector<glsl::vec2> const & normals,
|
||||
glsl::vec2 const & length, bool isLeft)
|
||||
auto const generateTriangles = [&joinsGeometry](glsl::vec3 const & pivot, vector<glsl::vec2> const & normals,
|
||||
glsl::vec2 const & length, bool isLeft)
|
||||
{
|
||||
float const eps = 1e-5;
|
||||
size_t const trianglesCount = normals.size() / 3;
|
||||
float const side = isLeft ? LEFT_SIDE : RIGHT_SIDE;
|
||||
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 ? CENTER : side);
|
||||
glsl::vec3 const len2 = glsl::vec3(length.x, length.y, glsl::length(normals[3 * j + 1]) < eps ? CENTER : side);
|
||||
glsl::vec3 const len3 = glsl::vec3(length.x, length.y, glsl::length(normals[3 * j + 2]) < eps ? CENTER : side);
|
||||
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);
|
||||
|
||||
m_joinsGeometry.push_back(RV(pivot, normals[3 * j], len1));
|
||||
m_joinsGeometry.push_back(RV(pivot, normals[3 * j + 1], len2));
|
||||
m_joinsGeometry.push_back(RV(pivot, normals[3 * j + 2], len3));
|
||||
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));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -91,15 +200,15 @@ void RouteShape::PrepareGeometry(ref_ptr<dp::TextureManager> textures)
|
|||
float const projRightStart = -segments[i].m_rightWidthScalar[StartPoint].y;
|
||||
float const projRightEnd = segments[i].m_rightWidthScalar[EndPoint].y;
|
||||
|
||||
m_geometry.push_back(RV(startPivot, glsl::vec2(0, 0), glsl::vec3(length, 0, CENTER)));
|
||||
m_geometry.push_back(RV(startPivot, leftNormalStart, glsl::vec3(length, projLeftStart, LEFT_SIDE)));
|
||||
m_geometry.push_back(RV(endPivot, glsl::vec2(0, 0), glsl::vec3(endLength, 0, CENTER)));
|
||||
m_geometry.push_back(RV(endPivot, leftNormalEnd, glsl::vec3(endLength, projLeftEnd, LEFT_SIDE)));
|
||||
geometry.push_back(RV(startPivot, glsl::vec2(0, 0), glsl::vec3(length, 0, kCenter)));
|
||||
geometry.push_back(RV(startPivot, leftNormalStart, glsl::vec3(length, projLeftStart, kLeftSide)));
|
||||
geometry.push_back(RV(endPivot, glsl::vec2(0, 0), glsl::vec3(endLength, 0, kCenter)));
|
||||
geometry.push_back(RV(endPivot, leftNormalEnd, glsl::vec3(endLength, projLeftEnd, kLeftSide)));
|
||||
|
||||
m_geometry.push_back(RV(startPivot, rightNormalStart, glsl::vec3(length, projRightStart, RIGHT_SIDE)));
|
||||
m_geometry.push_back(RV(startPivot, glsl::vec2(0, 0), glsl::vec3(length, 0, CENTER)));
|
||||
m_geometry.push_back(RV(endPivot, rightNormalEnd, glsl::vec3(endLength, projRightEnd, RIGHT_SIDE)));
|
||||
m_geometry.push_back(RV(endPivot, glsl::vec2(0, 0), glsl::vec3(endLength, 0, CENTER)));
|
||||
geometry.push_back(RV(startPivot, rightNormalStart, glsl::vec3(length, projRightStart, kRightSide)));
|
||||
geometry.push_back(RV(startPivot, glsl::vec2(0, 0), glsl::vec3(length, 0, kCenter)));
|
||||
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
|
||||
if (i < segments.size() - 1)
|
||||
|
@ -121,7 +230,7 @@ void RouteShape::PrepareGeometry(ref_ptr<dp::TextureManager> textures)
|
|||
}
|
||||
|
||||
// generate caps
|
||||
if (i == 0)
|
||||
if (isRoute && i == 0)
|
||||
{
|
||||
vector<glsl::vec2> normals;
|
||||
normals.reserve(24);
|
||||
|
@ -133,7 +242,7 @@ void RouteShape::PrepareGeometry(ref_ptr<dp::TextureManager> textures)
|
|||
glsl::vec2(length, 0), true);
|
||||
}
|
||||
|
||||
if (i == segments.size() - 1)
|
||||
if (isRoute && i == segments.size() - 1)
|
||||
{
|
||||
vector<glsl::vec2> normals;
|
||||
normals.reserve(24);
|
||||
|
@ -148,32 +257,33 @@ void RouteShape::PrepareGeometry(ref_ptr<dp::TextureManager> textures)
|
|||
length = endLength;
|
||||
}
|
||||
|
||||
m_length = length;
|
||||
outputLength = length;
|
||||
|
||||
// calculate joins bounds
|
||||
float const eps = 1e-5;
|
||||
double len = 0;
|
||||
for (size_t i = 0; i < segments.size() - 1; i++)
|
||||
if (!isRoute)
|
||||
{
|
||||
len += glsl::length(segments[i].m_points[EndPoint] - segments[i].m_points[StartPoint]);
|
||||
float const eps = 1e-5;
|
||||
double len = 0;
|
||||
for (size_t i = 0; i < segments.size() - 1; i++)
|
||||
{
|
||||
len += glsl::length(segments[i].m_points[EndPoint] - segments[i].m_points[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);
|
||||
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);
|
||||
|
||||
if (fabs(bounds.m_end - bounds.m_start) < eps)
|
||||
continue;
|
||||
if (fabs(bounds.m_end - bounds.m_start) < eps)
|
||||
continue;
|
||||
|
||||
bounds.m_offset = len;
|
||||
m_joinsBounds.push_back(bounds);
|
||||
bounds.m_offset = len;
|
||||
joinsBounds.push_back(bounds);
|
||||
}
|
||||
}
|
||||
|
||||
CacheEndOfRouteSign(textures);
|
||||
}
|
||||
|
||||
void RouteShape::CacheEndOfRouteSign(ref_ptr<dp::TextureManager> mng)
|
||||
void RouteShape::CacheEndOfRouteSign(ref_ptr<dp::TextureManager> mng, RouteData & routeData)
|
||||
{
|
||||
dp::TextureManager::SymbolRegion symbol;
|
||||
mng->GetSymbolRegion("route_to", symbol);
|
||||
|
@ -196,39 +306,82 @@ void RouteShape::CacheEndOfRouteSign(ref_ptr<dp::TextureManager> mng)
|
|||
|
||||
{
|
||||
dp::Batcher batcher(dp::Batcher::IndexPerQuad, dp::Batcher::VertexPerQuad);
|
||||
dp::SessionGuard guard(batcher, [this](dp::GLState const & state, drape_ptr<dp::RenderBucket> && b)
|
||||
dp::SessionGuard guard(batcher, [&routeData](dp::GLState const & state, drape_ptr<dp::RenderBucket> && b)
|
||||
{
|
||||
m_endOfRouteRenderBucket = move(b);
|
||||
m_endOfRouteState = state;
|
||||
routeData.m_endOfRouteSign.m_buckets.push_back(move(b));
|
||||
routeData.m_endOfRouteSign.m_state = state;
|
||||
});
|
||||
|
||||
dp::AttributeProvider provider(1 /*stream count*/, dp::Batcher::VertexPerQuad);
|
||||
provider.InitStream(0 /*stream index*/, gpu::SolidTexturingVertex::GetBindingInfo(), make_ref(data));
|
||||
dp::AttributeProvider provider(1 /* stream count */, dp::Batcher::VertexPerQuad);
|
||||
provider.InitStream(0 /* stream index */, gpu::SolidTexturingVertex::GetBindingInfo(), make_ref(data));
|
||||
|
||||
dp::IndicesRange indices = batcher.InsertTriangleStrip(state, make_ref(&provider), nullptr);
|
||||
ASSERT(indices.IsValid(), ());
|
||||
}
|
||||
}
|
||||
|
||||
void RouteShape::Draw(ref_ptr<dp::Batcher> batcher, ref_ptr<dp::TextureManager> textures)
|
||||
void RouteShape::Draw(ref_ptr<dp::TextureManager> textures, RouteData & routeData)
|
||||
{
|
||||
ASSERT(!m_geometry.empty(), ());
|
||||
|
||||
dp::TextureManager::SymbolRegion region;
|
||||
GetArrowTextureRegion(textures, region);
|
||||
|
||||
dp::GLState state(gpu::ROUTE_PROGRAM, dp::GLState::GeometryLayer);
|
||||
state.SetColorTexture(region.GetTexture());
|
||||
|
||||
dp::AttributeProvider provider(1, m_geometry.size());
|
||||
provider.InitStream(0, gpu::RouteVertex::GetBindingInfo(), make_ref(m_geometry.data()));
|
||||
batcher->InsertListOfStrip(state, make_ref(&provider), 4);
|
||||
|
||||
if (!m_joinsGeometry.empty())
|
||||
// route geometry
|
||||
{
|
||||
dp::AttributeProvider joinsProvider(1, m_joinsGeometry.size());
|
||||
joinsProvider.InitStream(0, gpu::RouteVertex::GetBindingInfo(), make_ref(m_joinsGeometry.data()));
|
||||
batcher->InsertTriangleList(state, make_ref(&joinsProvider));
|
||||
TGeometryBuffer geometry;
|
||||
TGeometryBuffer joinsGeometry;
|
||||
vector<RouteJoinBounds> bounds;
|
||||
PrepareGeometry(true /* isRoute */, m_polyline.GetPoints(), geometry, joinsGeometry, bounds, routeData.m_length);
|
||||
|
||||
dp::GLState state = dp::GLState(gpu::ROUTE_PROGRAM, dp::GLState::GeometryLayer);
|
||||
BatchGeometry(state, geometry, joinsGeometry, routeData.m_route);
|
||||
}
|
||||
|
||||
// arrows geometry
|
||||
if (!m_turns.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());
|
||||
|
||||
ClipArrowToSegments(m_turns, routeData);
|
||||
for (auto & renderProperty : routeData.m_arrows)
|
||||
{
|
||||
TGeometryBuffer geometry;
|
||||
TGeometryBuffer joinsGeometry;
|
||||
vector<m2::PointD> points = CalculatePoints(m_polyline, renderProperty->m_start, renderProperty->m_end);
|
||||
ASSERT_LESS_OR_EQUAL(points.size(), m_polyline.GetSize(), ());
|
||||
PrepareGeometry(false /* isRoute */, points, geometry, joinsGeometry, renderProperty->m_joinsBounds, routeData.m_length);
|
||||
BatchGeometry(state, geometry, joinsGeometry, renderProperty->m_arrow);
|
||||
}
|
||||
}
|
||||
|
||||
// end of route sign
|
||||
CacheEndOfRouteSign(textures, routeData);
|
||||
}
|
||||
|
||||
void RouteShape::BatchGeometry(dp::GLState const & state, TGeometryBuffer & geometry,
|
||||
TGeometryBuffer & joinsGeometry, RouteRenderProperty & property)
|
||||
{
|
||||
size_t const verticesCount = geometry.size() + joinsGeometry.size();
|
||||
if (verticesCount != 0)
|
||||
{
|
||||
dp::Batcher batcher(verticesCount, verticesCount);
|
||||
dp::SessionGuard guard(batcher, [&property](dp::GLState const & state, drape_ptr<dp::RenderBucket> && b)
|
||||
{
|
||||
property.m_buckets.push_back(move(b));
|
||||
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 (!joinsGeometry.empty())
|
||||
{
|
||||
dp::AttributeProvider joinsProvider(1 /* stream count */, joinsGeometry.size());
|
||||
joinsProvider.InitStream(0 /* stream index */, gpu::RouteVertex::GetBindingInfo(), make_ref(joinsGeometry.data()));
|
||||
batcher.InsertTriangleList(state, make_ref(&joinsProvider));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "drape/glstate.hpp"
|
||||
#include "drape/render_bucket.hpp"
|
||||
#include "drape/utils/vertex_decl.hpp"
|
||||
#include "drape/pointers.hpp"
|
||||
|
||||
#include "geometry/polyline2d.hpp"
|
||||
|
||||
|
@ -14,6 +15,8 @@
|
|||
namespace df
|
||||
{
|
||||
|
||||
double const kArrowSize = 0.001;
|
||||
|
||||
struct RouteJoinBounds
|
||||
{
|
||||
double m_start = 0;
|
||||
|
@ -21,37 +24,53 @@ struct RouteJoinBounds
|
|||
double m_offset = 0;
|
||||
};
|
||||
|
||||
struct RouteRenderProperty
|
||||
{
|
||||
dp::GLState m_state;
|
||||
vector<drape_ptr<dp::RenderBucket>> m_buckets;
|
||||
RouteRenderProperty() : m_state(0, dp::GLState::GeometryLayer) {}
|
||||
};
|
||||
|
||||
struct ArrowRenderProperty
|
||||
{
|
||||
vector<RouteJoinBounds> m_joinsBounds;
|
||||
vector<double> m_turns;
|
||||
double m_start;
|
||||
double m_end;
|
||||
RouteRenderProperty m_arrow;
|
||||
};
|
||||
|
||||
struct RouteData
|
||||
{
|
||||
dp::Color m_color;
|
||||
m2::RectF m_arrowTextureRect;
|
||||
double m_length;
|
||||
RouteRenderProperty m_route;
|
||||
vector<drape_ptr<ArrowRenderProperty>> m_arrows;
|
||||
RouteRenderProperty m_endOfRouteSign;
|
||||
};
|
||||
|
||||
class RouteShape
|
||||
{
|
||||
public:
|
||||
RouteShape(m2::PolylineD const & polyline,
|
||||
RouteShape(m2::PolylineD const & polyline, vector<double> const & turns,
|
||||
CommonViewParams const & params);
|
||||
|
||||
m2::RectF GetArrowTextureRect(ref_ptr<dp::TextureManager> textures) const;
|
||||
vector<RouteJoinBounds> const & GetJoinsBounds() const { return m_joinsBounds; }
|
||||
double GetLength() const { return m_length; }
|
||||
dp::GLState const & GetEndOfRouteState() const { return m_endOfRouteState; }
|
||||
drape_ptr<dp::RenderBucket> && MoveEndOfRouteRenderBucket() { return move(m_endOfRouteRenderBucket); }
|
||||
|
||||
void PrepareGeometry(ref_ptr<dp::TextureManager> textures);
|
||||
void Draw(ref_ptr<dp::Batcher> batcher, ref_ptr<dp::TextureManager> textures);
|
||||
void Draw(ref_ptr<dp::TextureManager> textures, RouteData & routeData);
|
||||
|
||||
private:
|
||||
using RV = gpu::RouteVertex;
|
||||
using TGeometryBuffer = buffer_vector<gpu::RouteVertex, 128>;
|
||||
|
||||
void CacheEndOfRouteSign(ref_ptr<dp::TextureManager> mng);
|
||||
|
||||
TGeometryBuffer m_geometry;
|
||||
TGeometryBuffer m_joinsGeometry;
|
||||
vector<RouteJoinBounds> m_joinsBounds;
|
||||
double m_length;
|
||||
void CacheEndOfRouteSign(ref_ptr<dp::TextureManager> mng, RouteData & routeData);
|
||||
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);
|
||||
|
||||
CommonViewParams m_params;
|
||||
m2::PolylineD m_polyline;
|
||||
|
||||
dp::GLState m_endOfRouteState;
|
||||
drape_ptr<dp::RenderBucket> m_endOfRouteRenderBucket;
|
||||
vector<double> m_turns;
|
||||
};
|
||||
|
||||
} // namespace df
|
||||
|
|
|
@ -1874,7 +1874,13 @@ void Framework::InsertRoute(Route const & route)
|
|||
for (size_t i = 0; i < turnsGeom.size(); i++)
|
||||
turns.push_back(turnsGeom[i].m_mercatorDistance);
|
||||
}
|
||||
m_drapeEngine->AddRoute(route.GetPoly(), turns, dp::Color(110, 180, 240, 160));
|
||||
|
||||
dp::Color routeColor;
|
||||
if (m_currentRouterType == RouterType::Pedestrian)
|
||||
routeColor = dp::Color(5, 105, 175, 204);
|
||||
else
|
||||
routeColor = dp::Color(30, 150, 240, 204);
|
||||
m_drapeEngine->AddRoute(route.GetPoly(), turns, routeColor);
|
||||
}
|
||||
|
||||
void Framework::CheckLocationForRouting(GpsInfo const & info)
|
||||
|
|
Loading…
Add table
Reference in a new issue