Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 13:58:55 +01:00
parent 4af19165ec
commit 68073add76
12458 changed files with 12350765 additions and 2 deletions

View file

@ -0,0 +1,222 @@
project(drape_frontend)
set(SRC
animation/animation.cpp
animation/animation.hpp
animation/arrow_animation.cpp
animation/arrow_animation.hpp
animation/base_interpolator.cpp
animation/base_interpolator.hpp
animation/follow_animation.cpp
animation/follow_animation.hpp
animation/interpolation_holder.cpp
animation/interpolation_holder.hpp
animation/interpolations.cpp
animation/interpolations.hpp
animation/interpolators.cpp
animation/interpolators.hpp
animation/linear_animation.cpp
animation/linear_animation.hpp
animation/opacity_animation.cpp
animation/opacity_animation.hpp
animation/parallel_animation.cpp
animation/parallel_animation.hpp
animation/scale_animation.cpp
animation/scale_animation.hpp
animation/sequence_animation.cpp
animation/sequence_animation.hpp
animation/show_hide_animation.cpp
animation/show_hide_animation.hpp
animation/value_mapping.hpp
animation_constants.hpp
animation_system.cpp
animation_system.hpp
animation_utils.cpp
animation_utils.hpp
apply_feature_functors.cpp
apply_feature_functors.hpp
area_shape.cpp
area_shape.hpp
arrow3d.cpp
arrow3d.hpp
backend_renderer.cpp
backend_renderer.hpp
base_renderer.cpp
base_renderer.hpp
batcher_bucket.hpp
batchers_pool.hpp
circles_pack_shape.cpp
circles_pack_shape.hpp
color_constants.cpp
color_constants.hpp
colored_symbol_shape.cpp
colored_symbol_shape.hpp
custom_features_context.hpp
debug_rect_renderer.cpp
debug_rect_renderer.hpp
drape_api.cpp
drape_api.hpp
drape_api_builder.cpp
drape_api_builder.hpp
drape_api_renderer.cpp
drape_api_renderer.hpp
drape_engine.cpp
drape_engine.hpp
drape_engine_params.hpp
drape_engine_safe_ptr.hpp
drape_hints.hpp
drape_notifier.cpp
drape_notifier.hpp
drape_measurer.cpp
drape_measurer.hpp
engine_context.cpp
engine_context.hpp
frame_values.hpp
frontend_renderer.cpp
frontend_renderer.hpp
gps_track_point.hpp
gps_track_renderer.cpp
gps_track_renderer.hpp
gui/choose_position_mark.cpp
gui/choose_position_mark.hpp
gui/compass.cpp
gui/compass.hpp
gui/copyright_label.cpp
gui/copyright_label.hpp
gui/debug_label.cpp
gui/debug_label.hpp
gui/drape_gui.cpp
gui/drape_gui.hpp
gui/gui_text.cpp
gui/gui_text.hpp
gui/layer_render.cpp
gui/layer_render.hpp
gui/ruler.cpp
gui/ruler.hpp
gui/ruler_helper.cpp
gui/ruler_helper.hpp
gui/scale_fps_helper.hpp
gui/shape.cpp
gui/shape.hpp
gui/skin.cpp
gui/skin.hpp
kinetic_scroller.cpp
kinetic_scroller.hpp
line_shape.cpp
line_shape.hpp
line_shape_helper.cpp
line_shape_helper.hpp
map_data_provider.cpp
map_data_provider.hpp
map_shape.hpp
message.cpp
message.hpp
message_acceptor.cpp
message_acceptor.hpp
message_queue.cpp
message_queue.hpp
message_subclasses.hpp
metaline_manager.cpp
metaline_manager.hpp
my_position.cpp
my_position.hpp
my_position_controller.cpp
my_position_controller.hpp
navigator.cpp
navigator.hpp
overlay_batcher.cpp
overlay_batcher.hpp
overlays_tracker.cpp
overlays_tracker.hpp
path_symbol_shape.cpp
path_symbol_shape.hpp
path_text_handle.cpp
path_text_handle.hpp
path_text_shape.cpp
path_text_shape.hpp
poi_symbol_shape.cpp
poi_symbol_shape.hpp
postprocess_renderer.cpp
postprocess_renderer.hpp
read_manager.cpp
read_manager.hpp
read_metaline_task.cpp
read_metaline_task.hpp
read_mwm_task.cpp
read_mwm_task.hpp
render_group.cpp
render_group.hpp
render_node.hpp
render_state_extension.cpp
render_state_extension.hpp
requested_tiles.cpp
requested_tiles.hpp
route_builder.cpp
route_builder.hpp
route_renderer.cpp
route_renderer.hpp
route_shape.cpp
route_shape.hpp
rule_drawer.cpp
rule_drawer.hpp
scenario_manager.cpp
scenario_manager.hpp
screen_animations.cpp
screen_animations.hpp
screen_operations.cpp
screen_operations.hpp
screen_quad_renderer.cpp
screen_quad_renderer.hpp
selection_shape.cpp
selection_shape.hpp
selection_shape_generator.cpp
selection_shape_generator.hpp
shape_view_params.hpp
stylist.cpp
stylist.hpp
text_handle.cpp
text_handle.hpp
text_layout.cpp
text_layout.hpp
text_shape.cpp
text_shape.hpp
threads_commutator.cpp
threads_commutator.hpp
tile_info.cpp
tile_info.hpp
tile_key.cpp
tile_key.hpp
tile_utils.cpp
tile_utils.hpp
traffic_generator.cpp
traffic_generator.hpp
traffic_renderer.cpp
traffic_renderer.hpp
transit_scheme_builder.cpp
transit_scheme_builder.hpp
transit_scheme_renderer.cpp
transit_scheme_renderer.hpp
user_event_stream.cpp
user_event_stream.hpp
user_mark_generator.cpp
user_mark_generator.hpp
user_mark_shapes.cpp
user_mark_shapes.hpp
user_marks_provider.cpp
user_marks_provider.hpp
visual_params.cpp
visual_params.hpp
)
omim_add_library(${PROJECT_NAME} ${SRC})
target_link_libraries(${PROJECT_NAME}
editor
drape
indexer
traffic
transit
shaders
)
omim_add_test_subdirectory(drape_frontend_tests)

View file

@ -0,0 +1,132 @@
#include "drape_frontend/animation/animation.hpp"
#include <algorithm>
namespace df
{
// static
bool Animation::GetCachedProperty(TPropertyCache const & properties, Object object, ObjectProperty property,
PropertyValue & value)
{
auto const it = properties.find(std::make_pair(object, property));
if (it != properties.end())
{
value = it->second;
return true;
}
return false;
}
// static
void Animation::GetCurrentScreen(TPropertyCache const & properties, ScreenBase const & screen,
ScreenBase & currentScreen)
{
currentScreen = screen;
if (!properties.empty())
{
double scale = currentScreen.GetScale();
double angle = currentScreen.GetAngle();
m2::PointD pos = currentScreen.GlobalRect().GlobalZero();
PropertyValue value;
if (GetCachedProperty(properties, Object::MapPlane, ObjectProperty::Scale, value))
scale = value.m_valueD;
if (GetCachedProperty(properties, Object::MapPlane, ObjectProperty::Angle, value))
angle = value.m_valueD;
if (GetCachedProperty(properties, Object::MapPlane, ObjectProperty::Position, value))
pos = value.m_valuePointD;
currentScreen.SetFromParams(pos, angle, scale);
}
}
bool Animation::HasSameObjects(Animation const & animation) const
{
TAnimObjects const & objects = animation.GetObjects();
for (auto const & object : objects)
if (HasObject(object))
return true;
return false;
}
bool Animation::CouldBeBlendedWith(Animation const & animation) const
{
return !HasSameObjects(animation) ||
((GetType() != animation.GetType()) && m_couldBeBlended && animation.m_couldBeBlended);
}
bool Animation::HasTargetProperty(Object object, ObjectProperty property) const
{
return HasProperty(object, property);
}
// static
bool Animation::GetMinDuration(Interpolator const & interpolator, double & minDuration)
{
if (interpolator.IsActive())
{
double const duration = interpolator.GetMinDuration();
if (duration >= 0.0)
minDuration = minDuration >= 0.0 ? std::min(duration, minDuration) : duration;
else
return false;
}
return true;
}
// static
bool Animation::GetMaxDuration(Interpolator const & interpolator, double & maxDuration)
{
if (interpolator.IsActive())
{
double const duration = interpolator.GetMaxDuration();
if (duration >= 0.0)
maxDuration = maxDuration >= 0.0 ? std::max(duration, maxDuration) : duration;
else
return false;
}
return true;
}
std::string DebugPrint(Animation::Type const & type)
{
switch (type)
{
case Animation::Type::Sequence: return "Sequence";
case Animation::Type::Parallel: return "Parallel";
case Animation::Type::MapLinear: return "MapLinear";
case Animation::Type::MapScale: return "MapScale";
case Animation::Type::MapFollow: return "MapFollow";
case Animation::Type::Arrow: return "Arrow";
case Animation::Type::KineticScroll: return "KineticScroll";
}
return "Unknown type";
}
std::string DebugPrint(Animation::Object const & object)
{
switch (object)
{
case Animation::Object::MyPositionArrow: return "MyPositionArrow";
case Animation::Object::MapPlane: return "MapPlane";
case Animation::Object::Selection: return "Selection";
}
return "Unknown object";
}
std::string DebugPrint(Animation::ObjectProperty const & property)
{
switch (property)
{
case Animation::ObjectProperty::Position: return "Position";
case Animation::ObjectProperty::Scale: return "Scale";
case Animation::ObjectProperty::Angle: return "Angle";
}
return "Unknown property";
}
} // namespace df

View file

@ -0,0 +1,169 @@
#pragma once
#include "drape_frontend/animation/interpolators.hpp"
#include "drape/pointers.hpp"
#include "geometry/point2d.hpp"
#include "geometry/screenbase.hpp"
#include <functional>
#include <map>
#include <set>
#include <string>
#include <utility>
namespace df
{
class Animation
{
public:
enum class Type
{
Sequence,
Parallel,
MapLinear,
MapScale,
MapFollow,
Arrow,
KineticScroll
};
enum class Object
{
MyPositionArrow,
MapPlane,
Selection
};
enum class ObjectProperty
{
Position,
Scale,
Angle
};
struct PropertyValue
{
enum class Type
{
ValueD,
ValuePointD
};
PropertyValue() {}
explicit PropertyValue(double value) : m_type(Type::ValueD), m_valueD(value) {}
explicit PropertyValue(m2::PointD const & value) : m_type(Type::ValuePointD), m_valuePointD(value) {}
Type m_type;
union
{
m2::PointD m_valuePointD;
double m_valueD;
};
};
using TAnimObjects = std::set<Object>;
using TObjectProperties = std::set<ObjectProperty>;
using TAction = std::function<void(ref_ptr<Animation>)>;
using TPropertyCache = std::map<std::pair<Object, ObjectProperty>, Animation::PropertyValue>;
Animation(bool couldBeInterrupted, bool couldBeBlended)
: m_couldBeInterrupted(couldBeInterrupted)
, m_couldBeBlended(couldBeBlended)
, m_interruptedOnCombine(false)
, m_couldBeRewinded(true)
{}
virtual ~Animation() = default;
virtual void Init(ScreenBase const & screen, TPropertyCache const & properties) {}
virtual void OnStart()
{
if (m_onStartAction != nullptr)
m_onStartAction(this);
}
virtual void OnFinish()
{
if (m_onFinishAction != nullptr)
m_onFinishAction(this);
}
virtual void Interrupt()
{
if (m_onInterruptAction != nullptr)
m_onInterruptAction(this);
}
virtual Type GetType() const = 0;
virtual std::string GetCustomType() const { return std::string(); }
virtual TAnimObjects const & GetObjects() const = 0;
virtual bool HasObject(Object object) const = 0;
virtual TObjectProperties const & GetProperties(Object object) const = 0;
virtual bool HasProperty(Object object, ObjectProperty property) const = 0;
virtual bool HasTargetProperty(Object object, ObjectProperty property) const;
static double constexpr kInvalidAnimationDuration = -1.0;
virtual void SetMaxDuration(double maxDuration) = 0;
virtual void SetMinDuration(double minDuration) = 0;
virtual double GetDuration() const = 0;
virtual double GetMaxDuration() const = 0;
virtual double GetMinDuration() const = 0;
virtual bool IsFinished() const = 0;
virtual void Advance(double elapsedSeconds) = 0;
virtual void Finish() { OnFinish(); }
virtual bool GetProperty(Object object, ObjectProperty property, PropertyValue & value) const = 0;
virtual bool GetTargetProperty(Object object, ObjectProperty property, PropertyValue & value) const = 0;
void SetOnStartAction(TAction const & action) { m_onStartAction = action; }
void SetOnFinishAction(TAction const & action) { m_onFinishAction = action; }
void SetOnInterruptAction(TAction const & action) { m_onInterruptAction = action; }
bool CouldBeBlended() const { return m_couldBeBlended; }
bool CouldBeInterrupted() const { return m_couldBeInterrupted; }
bool CouldBeBlendedWith(Animation const & animation) const;
bool HasSameObjects(Animation const & animation) const;
void SetInterruptedOnCombine(bool enable) { m_interruptedOnCombine = enable; }
bool GetInterruptedOnCombine() const { return m_interruptedOnCombine; }
void SetCouldBeInterrupted(bool enable) { m_couldBeInterrupted = enable; }
void SetCouldBeBlended(bool enable) { m_couldBeBlended = enable; }
void SetCouldBeRewinded(bool enable) { m_couldBeRewinded = enable; }
bool CouldBeRewinded() const { return m_couldBeRewinded; }
protected:
static void GetCurrentScreen(TPropertyCache const & properties, ScreenBase const & screen,
ScreenBase & currentScreen);
static bool GetCachedProperty(TPropertyCache const & properties, Object object, ObjectProperty property,
PropertyValue & value);
static bool GetMinDuration(Interpolator const & interpolator, double & minDuration);
static bool GetMaxDuration(Interpolator const & interpolator, double & maxDuration);
TAction m_onStartAction;
TAction m_onFinishAction;
TAction m_onInterruptAction;
// Animation could be interrupted in case of blending impossibility.
bool m_couldBeInterrupted;
// Animation could be blended with other animations.
bool m_couldBeBlended;
// Animation must be interrupted in case of combining another animation.
bool m_interruptedOnCombine;
// Animation could be rewinded in case of finishing.
bool m_couldBeRewinded;
};
std::string DebugPrint(Animation::Type const & type);
std::string DebugPrint(Animation::Object const & object);
std::string DebugPrint(Animation::ObjectProperty const & property);
} // namespace df

View file

@ -0,0 +1,185 @@
#include "drape_frontend/animation/arrow_animation.hpp"
#include <algorithm>
namespace df
{
ArrowAnimation::ArrowAnimation(m2::PointD const & startPos, m2::PointD const & endPos, double moveDuration,
double startAngle, double endAngle)
: Animation(true /* couldBeInterrupted */, true /* couldBeBlended */)
, m_positionInterpolator(moveDuration, 0.0 /* delay */, startPos, endPos)
, m_angleInterpolator(startAngle, endAngle)
{
m_objects.insert(Animation::Object::MyPositionArrow);
if (m_positionInterpolator.IsActive())
m_properties.insert(Animation::ObjectProperty::Position);
if (m_angleInterpolator.IsActive())
m_properties.insert(Animation::ObjectProperty::Angle);
}
void ArrowAnimation::Init(ScreenBase const & screen, TPropertyCache const & properties)
{
PropertyValue value;
double minDuration;
double maxDuration;
if (GetCachedProperty(properties, Animation::Object::MyPositionArrow, Animation::ObjectProperty::Position, value))
{
minDuration = m_positionInterpolator.GetMinDuration();
maxDuration = m_positionInterpolator.GetMaxDuration();
m_positionInterpolator = PositionInterpolator(m_positionInterpolator.GetDuration(), 0.0 /* delay */,
value.m_valuePointD, m_positionInterpolator.GetTargetPosition());
m_positionInterpolator.SetMinDuration(minDuration);
m_positionInterpolator.SetMaxDuration(maxDuration);
if (m_positionInterpolator.IsActive())
m_properties.insert(Animation::ObjectProperty::Position);
}
if (GetCachedProperty(properties, Animation::Object::MyPositionArrow, Animation::ObjectProperty::Angle, value))
{
minDuration = m_angleInterpolator.GetMinDuration();
maxDuration = m_angleInterpolator.GetMaxDuration();
m_angleInterpolator = AngleInterpolator(value.m_valueD, m_angleInterpolator.GetTargetAngle());
m_angleInterpolator.SetMinDuration(minDuration);
m_angleInterpolator.SetMaxDuration(maxDuration);
if (m_angleInterpolator.IsActive())
m_properties.insert(Animation::ObjectProperty::Angle);
}
}
Animation::TAnimObjects const & ArrowAnimation::GetObjects() const
{
return m_objects;
}
bool ArrowAnimation::HasObject(Object object) const
{
return object == Animation::Object::MyPositionArrow;
}
Animation::TObjectProperties const & ArrowAnimation::GetProperties(Object object) const
{
return m_properties;
}
bool ArrowAnimation::HasProperty(Object object, ObjectProperty property) const
{
return HasObject(object) && m_properties.find(property) != m_properties.end();
}
void ArrowAnimation::Advance(double elapsedSeconds)
{
if (m_positionInterpolator.IsActive())
m_positionInterpolator.Advance(elapsedSeconds);
if (m_angleInterpolator.IsActive())
m_angleInterpolator.Advance(elapsedSeconds);
}
void ArrowAnimation::Finish()
{
if (m_positionInterpolator.IsActive())
m_positionInterpolator.Finish();
if (m_angleInterpolator.IsActive())
m_angleInterpolator.Finish();
}
void ArrowAnimation::SetMaxDuration(double maxDuration)
{
if (m_positionInterpolator.IsActive())
m_positionInterpolator.SetMaxDuration(maxDuration);
if (m_angleInterpolator.IsActive())
m_angleInterpolator.SetMaxDuration(maxDuration);
}
void ArrowAnimation::SetMinDuration(double minDuration)
{
if (m_positionInterpolator.IsActive())
m_positionInterpolator.SetMinDuration(minDuration);
if (m_angleInterpolator.IsActive())
m_angleInterpolator.SetMinDuration(minDuration);
}
double ArrowAnimation::GetDuration() const
{
double duration = 0.0;
if (m_angleInterpolator.IsActive())
duration = m_angleInterpolator.GetDuration();
if (m_positionInterpolator.IsActive())
duration = std::max(duration, m_positionInterpolator.GetDuration());
return duration;
}
double ArrowAnimation::GetMaxDuration() const
{
double maxDuration = Animation::kInvalidAnimationDuration;
if (!Animation::GetMaxDuration(m_angleInterpolator, maxDuration) ||
!Animation::GetMaxDuration(m_positionInterpolator, maxDuration))
return Animation::kInvalidAnimationDuration;
return maxDuration;
}
double ArrowAnimation::GetMinDuration() const
{
double minDuration = Animation::kInvalidAnimationDuration;
if (!Animation::GetMinDuration(m_angleInterpolator, minDuration) ||
!Animation::GetMinDuration(m_positionInterpolator, minDuration))
return Animation::kInvalidAnimationDuration;
return minDuration;
}
bool ArrowAnimation::IsFinished() const
{
return m_positionInterpolator.IsFinished() && m_angleInterpolator.IsFinished();
}
bool ArrowAnimation::GetProperty(Object object, ObjectProperty property, PropertyValue & value) const
{
return GetProperty(object, property, false /* targetValue */, value);
}
bool ArrowAnimation::GetTargetProperty(Object object, ObjectProperty property, PropertyValue & value) const
{
return GetProperty(object, property, true /* targetValue */, value);
}
bool ArrowAnimation::GetProperty(Object object, ObjectProperty property, bool targetValue, PropertyValue & value) const
{
ASSERT_EQUAL(static_cast<int>(object), static_cast<int>(Animation::Object::MyPositionArrow), ());
switch (property)
{
case Animation::ObjectProperty::Position:
if (m_positionInterpolator.IsActive())
{
value = PropertyValue(targetValue ? m_positionInterpolator.GetTargetPosition()
: m_positionInterpolator.GetPosition());
return true;
}
return false;
case Animation::ObjectProperty::Angle:
if (m_angleInterpolator.IsActive())
{
value = PropertyValue(targetValue ? m_angleInterpolator.GetTargetAngle() : m_angleInterpolator.GetAngle());
return true;
}
return false;
default: ASSERT(false, ("Wrong property:", static_cast<int>(property)));
}
return false;
}
} // namespace df

View file

@ -0,0 +1,46 @@
#pragma once
#include "animation.hpp"
#include "interpolators.hpp"
namespace df
{
class ArrowAnimation : public Animation
{
public:
ArrowAnimation(m2::PointD const & startPos, m2::PointD const & endPos, double moveDuration, double startAngle,
double endAngle);
void Init(ScreenBase const & screen, TPropertyCache const & properties) override;
Animation::Type GetType() const override { return Animation::Type::Arrow; }
TAnimObjects const & GetObjects() const override;
bool HasObject(Object object) const override;
TObjectProperties const & GetProperties(Object object) const override;
bool HasProperty(Object object, ObjectProperty property) const override;
void SetMaxDuration(double maxDuration) override;
void SetMinDuration(double minDuration) override;
double GetDuration() const override;
double GetMinDuration() const override;
double GetMaxDuration() const override;
bool IsFinished() const override;
void Advance(double elapsedSeconds) override;
void Finish() override;
bool GetProperty(Object object, ObjectProperty property, PropertyValue & value) const override;
bool GetTargetProperty(Object object, ObjectProperty property, PropertyValue & value) const override;
private:
bool GetProperty(Object object, ObjectProperty property, bool targetValue, PropertyValue & value) const;
TAnimObjects m_objects;
TObjectProperties m_properties;
PositionInterpolator m_positionInterpolator;
AngleInterpolator m_angleInterpolator;
};
} // namespace df

View file

@ -0,0 +1,52 @@
#include "drape_frontend/animation/base_interpolator.hpp"
#include "drape_frontend/animation/interpolation_holder.hpp"
#include "base/assert.hpp"
#include "base/math.hpp"
#include <algorithm>
namespace df
{
BaseInterpolator::BaseInterpolator(double duration, double delay)
: m_elapsedTime(0.0)
, m_duration(duration)
, m_delay(delay)
{
ASSERT(m_duration > 0.0, ());
InterpolationHolder::Instance().RegisterInterpolator(this);
}
BaseInterpolator::~BaseInterpolator()
{
InterpolationHolder::Instance().DeregisterInterpolator(this);
}
bool BaseInterpolator::IsFinished() const
{
return m_elapsedTime > (m_duration + m_delay);
}
void BaseInterpolator::Advance(double elapsedSeconds)
{
m_elapsedTime += elapsedSeconds;
}
double BaseInterpolator::GetT() const
{
if (IsFinished())
return 1.0;
return std::max(m_elapsedTime - m_delay, 0.0) / m_duration;
}
double BaseInterpolator::GetElapsedTime() const
{
return m_elapsedTime;
}
double BaseInterpolator::GetDuration() const
{
return m_duration;
}
} // namespace df

View file

@ -0,0 +1,24 @@
#pragma once
namespace df
{
class BaseInterpolator
{
public:
explicit BaseInterpolator(double duration, double delay = 0);
virtual ~BaseInterpolator();
bool IsFinished() const;
virtual void Advance(double elapsedSeconds);
protected:
double GetT() const;
double GetElapsedTime() const;
double GetDuration() const;
private:
double m_elapsedTime;
double m_duration;
double m_delay;
};
} // namespace df

View file

@ -0,0 +1,232 @@
#include "drape_frontend/animation/follow_animation.hpp"
#include "drape_frontend/animation_constants.hpp"
#include "drape_frontend/animation_system.hpp"
#include "base/assert.hpp"
#include "base/logging.hpp"
#include <algorithm>
namespace df
{
MapFollowAnimation::MapFollowAnimation(ScreenBase const & screen, m2::PointD const & globalUserPosition,
m2::PointD const & endPixelPosition, double endScale, double endAngle,
bool isAutoZoom)
: Animation(true /* couldBeInterrupted */, true /* couldBeBlended */)
, m_isAutoZoom(isAutoZoom)
, m_globalPosition(globalUserPosition)
, m_endPixelPosition(endPixelPosition)
, m_endScale(endScale)
, m_endAngle(endAngle)
{
TPropertyCache properties;
Init(screen, properties);
}
void MapFollowAnimation::Init(ScreenBase const & screen, TPropertyCache const & properties)
{
ScreenBase currentScreen;
GetCurrentScreen(properties, screen, currentScreen);
double minDuration = m_offsetInterpolator.GetMinDuration();
double maxDuration = m_offsetInterpolator.GetMaxDuration();
m_offset = currentScreen.PtoG(currentScreen.P3dtoP(m_endPixelPosition)) - m_globalPosition;
double const averageScale = m_isAutoZoom ? currentScreen.GetScale() : (currentScreen.GetScale() + m_endScale) / 2.0;
double const moveDuration =
PositionInterpolator::GetMoveDuration(m_offset.Length(), screen.PixelRectIn3d(), averageScale);
m_offsetInterpolator = PositionInterpolator(moveDuration, 0.0, m_offset, m2::PointD(0.0, 0.0));
m_offsetInterpolator.SetMinDuration(minDuration);
m_offsetInterpolator.SetMaxDuration(maxDuration);
maxDuration = m_scaleInterpolator.GetMaxDuration();
m_scaleInterpolator = ScaleInterpolator(currentScreen.GetScale(), m_endScale, m_isAutoZoom);
m_scaleInterpolator.SetMaxDuration(maxDuration);
maxDuration = m_angleInterpolator.GetMaxDuration();
m_angleInterpolator = AngleInterpolator(currentScreen.GetAngle(), m_endAngle);
m_angleInterpolator.SetMaxDuration(maxDuration);
double const duration = CalculateDuration();
m_scaleInterpolator.SetMinDuration(duration);
m_angleInterpolator.SetMinDuration(duration);
m_objects.insert(Animation::Object::MapPlane);
if (m_scaleInterpolator.IsActive())
m_properties.insert(Animation::ObjectProperty::Scale);
if (m_angleInterpolator.IsActive())
m_properties.insert(Animation::ObjectProperty::Angle);
if (m_offsetInterpolator.IsActive() || m_scaleInterpolator.IsActive() || m_angleInterpolator.IsActive())
m_properties.insert(Animation::ObjectProperty::Position);
// If MapFollowAnimation affects only angles, disable rewinding.
SetCouldBeRewinded(!m_angleInterpolator.IsActive() || m_scaleInterpolator.IsActive() ||
m_offsetInterpolator.IsActive());
}
Animation::TObjectProperties const & MapFollowAnimation::GetProperties(Object object) const
{
ASSERT_EQUAL(static_cast<int>(object), static_cast<int>(Animation::Object::MapPlane), ());
return m_properties;
}
bool MapFollowAnimation::HasProperty(Object object, ObjectProperty property) const
{
return HasObject(object) && m_properties.find(property) != m_properties.end();
}
void MapFollowAnimation::Advance(double elapsedSeconds)
{
if (m_angleInterpolator.IsActive())
m_angleInterpolator.Advance(elapsedSeconds);
if (m_scaleInterpolator.IsActive())
m_scaleInterpolator.Advance(elapsedSeconds);
if (m_offsetInterpolator.IsActive())
m_offsetInterpolator.Advance(elapsedSeconds);
}
void MapFollowAnimation::Finish()
{
if (m_angleInterpolator.IsActive())
m_angleInterpolator.Finish();
if (m_scaleInterpolator.IsActive())
{
if (m_isAutoZoom)
m_scaleInterpolator.SetActive(false);
else
m_scaleInterpolator.Finish();
}
if (m_offsetInterpolator.IsActive())
m_offsetInterpolator.Finish();
Animation::Finish();
}
void MapFollowAnimation::SetMaxDuration(double maxDuration)
{
if (m_angleInterpolator.IsActive())
m_angleInterpolator.SetMaxDuration(maxDuration);
if (!m_isAutoZoom && m_scaleInterpolator.IsActive())
m_scaleInterpolator.SetMaxDuration(maxDuration);
if (m_offsetInterpolator.IsActive())
m_offsetInterpolator.SetMaxDuration(maxDuration);
}
void MapFollowAnimation::SetMinDuration(double minDuration)
{
if (m_angleInterpolator.IsActive())
m_angleInterpolator.SetMinDuration(minDuration);
if (!m_isAutoZoom && m_scaleInterpolator.IsActive())
m_scaleInterpolator.SetMinDuration(minDuration);
if (m_offsetInterpolator.IsActive())
m_offsetInterpolator.SetMinDuration(minDuration);
}
double MapFollowAnimation::GetDuration() const
{
return CalculateDuration();
}
double MapFollowAnimation::GetMaxDuration() const
{
double maxDuration = Animation::kInvalidAnimationDuration;
if (!Animation::GetMaxDuration(m_angleInterpolator, maxDuration) ||
(!m_isAutoZoom && !Animation::GetMaxDuration(m_scaleInterpolator, maxDuration)) ||
!Animation::GetMaxDuration(m_offsetInterpolator, maxDuration))
return Animation::kInvalidAnimationDuration;
return maxDuration;
}
double MapFollowAnimation::GetMinDuration() const
{
double minDuration = Animation::kInvalidAnimationDuration;
if (!Animation::GetMinDuration(m_angleInterpolator, minDuration) ||
(!m_isAutoZoom && !Animation::GetMinDuration(m_scaleInterpolator, minDuration)) ||
!Animation::GetMinDuration(m_offsetInterpolator, minDuration))
return Animation::kInvalidAnimationDuration;
return minDuration;
}
double MapFollowAnimation::CalculateDuration() const
{
double duration = std::max(m_angleInterpolator.GetDuration(), m_offsetInterpolator.GetDuration());
if (!m_isAutoZoom)
duration = std::max(duration, m_scaleInterpolator.GetDuration());
return duration;
}
bool MapFollowAnimation::IsFinished() const
{
return m_angleInterpolator.IsFinished() && m_scaleInterpolator.IsFinished() && m_offsetInterpolator.IsFinished();
}
bool MapFollowAnimation::GetProperty(Object object, ObjectProperty property, PropertyValue & value) const
{
return GetProperty(object, property, false /* targetValue */, value);
}
bool MapFollowAnimation::GetTargetProperty(Object object, ObjectProperty property, PropertyValue & value) const
{
return GetProperty(object, property, true /* targetValue */, value);
}
bool MapFollowAnimation::GetProperty(Object object, ObjectProperty property, bool targetValue,
PropertyValue & value) const
{
if (property == Animation::ObjectProperty::Position)
{
ScreenBase tmp = AnimationSystem::Instance().GetLastScreen();
if (targetValue)
{
tmp.SetFromParams(m_globalPosition, m_angleInterpolator.GetTargetAngle(),
m_isAutoZoom ? m_scaleInterpolator.GetScale() : m_scaleInterpolator.GetTargetScale());
tmp.MatchGandP3d(m_globalPosition, m_endPixelPosition);
}
else
{
double const scale = m_scaleInterpolator.GetScale() / m_scaleInterpolator.GetStartScale();
double const angle = m_angleInterpolator.GetAngle() - m_angleInterpolator.GetStartAngle();
m2::PointD offset = m_offsetInterpolator.GetPosition() * scale;
offset.Rotate(angle);
m2::PointD pos = m_globalPosition + offset;
tmp.SetFromParams(m_globalPosition, m_angleInterpolator.GetAngle(), m_scaleInterpolator.GetScale());
tmp.MatchGandP3d(pos, m_endPixelPosition);
}
value = PropertyValue(tmp.GetOrg());
return true;
}
if (property == Animation::ObjectProperty::Angle)
{
value = PropertyValue(targetValue ? m_angleInterpolator.GetTargetAngle() : m_angleInterpolator.GetAngle());
return true;
}
if (property == Animation::ObjectProperty::Scale)
{
value = PropertyValue((targetValue && !m_isAutoZoom) ? m_scaleInterpolator.GetTargetScale()
: m_scaleInterpolator.GetScale());
return true;
}
ASSERT(false, ("Wrong property:", static_cast<int>(property)));
return false;
}
bool MapFollowAnimation::HasScale() const
{
return m_scaleInterpolator.IsActive();
}
bool MapFollowAnimation::HasPixelOffset() const
{
return m_offsetInterpolator.IsActive();
}
} // namespace df

View file

@ -0,0 +1,64 @@
#pragma once
#include "animation.hpp"
#include "interpolators.hpp"
namespace df
{
class MapFollowAnimation : public Animation
{
public:
MapFollowAnimation(ScreenBase const & screen, m2::PointD const & globalUserPosition,
m2::PointD const & endPixelPosition, double endScale, double endAngle, bool isAutoZoom);
void Init(ScreenBase const & screen, TPropertyCache const & properties) override;
Animation::Type GetType() const override { return Animation::Type::MapFollow; }
TAnimObjects const & GetObjects() const override { return m_objects; }
bool HasObject(Object object) const override { return object == Animation::Object::MapPlane; }
TObjectProperties const & GetProperties(Object object) const override;
bool HasProperty(Object object, ObjectProperty property) const override;
void Advance(double elapsedSeconds) override;
void Finish() override;
void SetMaxDuration(double maxDuration) override;
void SetMinDuration(double minDuration) override;
double GetDuration() const override;
double GetMinDuration() const override;
double GetMaxDuration() const override;
bool IsFinished() const override;
bool GetProperty(Object object, ObjectProperty property, PropertyValue & value) const override;
bool GetTargetProperty(Object object, ObjectProperty property, PropertyValue & value) const override;
bool IsAutoZoom() const { return m_isAutoZoom; }
bool HasScale() const;
bool HasPixelOffset() const;
private:
bool GetProperty(Object object, ObjectProperty property, bool targetValue, PropertyValue & value) const;
double CalculateDuration() const;
bool m_isAutoZoom;
ScaleInterpolator m_scaleInterpolator;
AngleInterpolator m_angleInterpolator;
PositionInterpolator m_offsetInterpolator;
m2::PointD const m_globalPosition;
m2::PointD const m_endPixelPosition;
double const m_endScale;
double const m_endAngle;
m2::PointD m_offset;
TObjectProperties m_properties;
TAnimObjects m_objects;
};
} // namespace df

View file

@ -0,0 +1,46 @@
#include "drape_frontend/animation/interpolation_holder.hpp"
#include "drape_frontend/animation/base_interpolator.hpp"
#include "base/assert.hpp"
namespace df
{
InterpolationHolder & InterpolationHolder::Instance()
{
static InterpolationHolder holder;
return holder;
}
bool InterpolationHolder::IsActive() const
{
return !m_interpolations.empty();
}
void InterpolationHolder::Advance(double elapsedSeconds)
{
auto iter = m_interpolations.begin();
while (iter != m_interpolations.end())
{
(*iter)->Advance(elapsedSeconds);
if ((*iter)->IsFinished())
iter = m_interpolations.erase(iter);
else
++iter;
}
}
InterpolationHolder::~InterpolationHolder()
{
ASSERT(m_interpolations.empty(), ());
}
void InterpolationHolder::RegisterInterpolator(BaseInterpolator * interpolator)
{
VERIFY(m_interpolations.insert(interpolator).second, ());
}
void InterpolationHolder::DeregisterInterpolator(BaseInterpolator * interpolator)
{
m_interpolations.erase(interpolator);
}
} // namespace df

View file

@ -0,0 +1,31 @@
#pragma once
#include "base/macros.hpp"
#include <set>
namespace df
{
class BaseInterpolator;
class InterpolationHolder
{
public:
static InterpolationHolder & Instance();
bool IsActive() const;
void Advance(double elapsedSeconds);
private:
InterpolationHolder() = default;
~InterpolationHolder();
DISALLOW_COPY_AND_MOVE(InterpolationHolder);
private:
friend class BaseInterpolator;
void RegisterInterpolator(BaseInterpolator * interpolator);
void DeregisterInterpolator(BaseInterpolator * interpolator);
using TInterpolatorSet = std::set<BaseInterpolator *>;
TInterpolatorSet m_interpolations;
};
} // namespace df

View file

@ -0,0 +1,26 @@
#include "drape_frontend/animation/interpolations.hpp"
#include "geometry/angles.hpp"
namespace df
{
double InterpolateDouble(double startV, double endV, double t)
{
return startV + (endV - startV) * t;
}
m2::PointD InterpolatePoint(m2::PointD const & startPt, m2::PointD const & endPt, double t)
{
m2::PointD diff = endPt - startPt;
return startPt + diff * t;
}
double InterpolateAngle(double startAngle, double endAngle, double t)
{
startAngle = ang::AngleIn2PI(startAngle);
endAngle = ang::AngleIn2PI(endAngle);
return startAngle + ang::GetShortestDistance(startAngle, endAngle) * t;
}
} // namespace df

View file

@ -0,0 +1,14 @@
#pragma once
#include "geometry/any_rect2d.hpp"
#include "geometry/point2d.hpp"
#include "geometry/rect2d.hpp"
namespace df
{
double InterpolateDouble(double startV, double endV, double t);
m2::PointD InterpolatePoint(m2::PointD const & startPt, m2::PointD const & endPt, double t);
double InterpolateAngle(double startAngle, double endAngle, double t);
} // namespace df

View file

@ -0,0 +1,279 @@
#include "drape_frontend/animation/interpolators.hpp"
#include "drape_frontend/animation/interpolations.hpp"
#include "base/assert.hpp"
#include <algorithm>
namespace df
{
double CalcAnimSpeedDuration(double pxDiff, double pxSpeed)
{
double constexpr kEps = 1e-5;
if (AlmostEqualAbs(pxDiff, 0.0, kEps))
return 0.0;
return fabs(pxDiff) / pxSpeed;
}
Interpolator::Interpolator(double duration, double delay)
: m_elapsedTime(0.0)
, m_duration(duration)
, m_maxDuration(Interpolator::kInvalidDuration)
, m_minDuration(Interpolator::kInvalidDuration)
, m_delay(delay)
, m_isActive(false)
{
ASSERT_GREATER_OR_EQUAL(m_duration, 0.0, ());
}
bool Interpolator::IsFinished() const
{
if (!IsActive())
return true;
return m_elapsedTime > (m_duration + m_delay);
}
void Interpolator::Advance(double elapsedSeconds)
{
m_elapsedTime += elapsedSeconds;
}
void Interpolator::Finish()
{
m_elapsedTime = m_duration + m_delay + 1.0;
}
bool Interpolator::IsActive() const
{
return m_isActive;
}
void Interpolator::SetActive(bool active)
{
m_isActive = active;
}
void Interpolator::SetMaxDuration(double maxDuration)
{
m_maxDuration = maxDuration;
if (m_maxDuration >= 0.0)
m_duration = std::min(m_duration, m_maxDuration);
}
void Interpolator::SetMinDuration(double minDuration)
{
m_minDuration = minDuration;
if (m_minDuration >= 0.0)
m_duration = std::max(m_duration, m_minDuration);
}
double Interpolator::GetMaxDuration() const
{
return m_maxDuration;
}
double Interpolator::GetMinDuration() const
{
return m_minDuration;
}
double Interpolator::GetT() const
{
if (IsFinished())
return 1.0;
return std::max(m_elapsedTime - m_delay, 0.0) / m_duration;
}
double Interpolator::GetElapsedTime() const
{
return m_elapsedTime;
}
double Interpolator::GetDuration() const
{
return m_duration;
}
PositionInterpolator::PositionInterpolator()
: PositionInterpolator(0.0 /* duration */, 0.0 /* delay */, m2::PointD(), m2::PointD())
{}
PositionInterpolator::PositionInterpolator(double duration, double delay, m2::PointD const & startPosition,
m2::PointD const & endPosition)
: Interpolator(duration, delay)
, m_startPosition(startPosition)
, m_endPosition(endPosition)
, m_position(startPosition)
{
SetActive((GetDuration() > 0.0) && (m_startPosition != m_endPosition));
}
PositionInterpolator::PositionInterpolator(m2::PointD const & startPosition, m2::PointD const & endPosition,
ScreenBase const & convertor)
: PositionInterpolator(0.0 /* delay */, startPosition, endPosition, convertor)
{}
PositionInterpolator::PositionInterpolator(double delay, m2::PointD const & startPosition,
m2::PointD const & endPosition, ScreenBase const & convertor)
: Interpolator(PositionInterpolator::GetMoveDuration(startPosition, endPosition, convertor), delay)
, m_startPosition(startPosition)
, m_endPosition(endPosition)
, m_position(startPosition)
{
SetActive((GetDuration() > 0.0) && (m_startPosition != m_endPosition));
}
PositionInterpolator::PositionInterpolator(m2::PointD const & startPosition, m2::PointD const & endPosition,
m2::RectD const & viewportRect, double scale)
: PositionInterpolator(0.0 /* delay */, startPosition, endPosition, viewportRect, scale)
{}
PositionInterpolator::PositionInterpolator(double delay, m2::PointD const & startPosition,
m2::PointD const & endPosition, m2::RectD const & viewportRect, double scale)
: Interpolator(PositionInterpolator::GetMoveDuration(startPosition, endPosition, viewportRect, scale), delay)
, m_startPosition(startPosition)
, m_endPosition(endPosition)
, m_position(startPosition)
{
SetActive((GetDuration() > 0.0) && (m_startPosition != m_endPosition));
}
// static
double PositionInterpolator::GetMoveDuration(double globalDistance, m2::RectD const & viewportRect, double scale)
{
double constexpr kMinMoveDuration = 0.2;
double constexpr kMinSpeedScalar = 0.2;
double constexpr kMaxSpeedScalar = 7.0;
double constexpr kEps = 1e-5;
ASSERT_GREATER(scale, 0.0, ());
double const pixelLength = globalDistance / scale;
if (pixelLength < kEps)
return 0.0;
double const minSize = std::min(viewportRect.SizeX(), viewportRect.SizeY());
if (pixelLength < kMinSpeedScalar * minSize)
return kMinMoveDuration;
double const pixelSpeed = kMaxSpeedScalar * minSize;
return CalcAnimSpeedDuration(pixelLength, pixelSpeed);
}
// static
double PositionInterpolator::GetMoveDuration(m2::PointD const & startPosition, m2::PointD const & endPosition,
m2::RectD const & viewportRect, double scale)
{
return GetMoveDuration(endPosition.Length(startPosition), viewportRect, scale);
}
// static
double PositionInterpolator::GetMoveDuration(m2::PointD const & startPosition, m2::PointD const & endPosition,
ScreenBase const & convertor)
{
return GetMoveDuration(startPosition, endPosition, convertor.PixelRectIn3d(), convertor.GetScale());
}
void PositionInterpolator::Advance(double elapsedSeconds)
{
TBase::Advance(elapsedSeconds);
m_position = InterpolatePoint(m_startPosition, m_endPosition, GetT());
}
void PositionInterpolator::Finish()
{
TBase::Finish();
m_position = m_endPosition;
}
ScaleInterpolator::ScaleInterpolator()
: ScaleInterpolator(1.0 /* startScale */, 1.0 /* endScale */, false /* isAutoZoom */)
{}
ScaleInterpolator::ScaleInterpolator(double startScale, double endScale, bool isAutoZoom)
: ScaleInterpolator(0.0 /* delay */, startScale, endScale, isAutoZoom)
{}
ScaleInterpolator::ScaleInterpolator(double delay, double startScale, double endScale, bool isAutoZoom)
: Interpolator(ScaleInterpolator::GetScaleDuration(startScale, endScale, isAutoZoom), delay)
, m_startScale(startScale)
, m_endScale(endScale)
, m_scale(startScale)
{
SetActive((GetDuration() > 0.0) && (m_startScale != m_endScale));
}
// static
double ScaleInterpolator::GetScaleDuration(double startScale, double endScale, bool isAutoZoom)
{
// Resize 2.0 times should be done for 1.2 seconds in autozoom or for 0.2 seconds in usual case.
double const kPixelSpeed = isAutoZoom ? (2.0 / 1.2) : (2.0 / 0.2);
if (startScale > endScale)
std::swap(startScale, endScale);
return CalcAnimSpeedDuration(endScale / startScale, kPixelSpeed);
}
void ScaleInterpolator::Advance(double elapsedSeconds)
{
TBase::Advance(elapsedSeconds);
m_scale = InterpolateDouble(m_startScale, m_endScale, GetT());
}
void ScaleInterpolator::Finish()
{
TBase::Finish();
m_scale = m_endScale;
}
AngleInterpolator::AngleInterpolator() : AngleInterpolator(0.0 /* startAngle */, 0.0 /* endAngle */) {}
AngleInterpolator::AngleInterpolator(double startAngle, double endAngle)
: AngleInterpolator(0.0 /* delay */, startAngle, endAngle)
{}
AngleInterpolator::AngleInterpolator(double delay, double startAngle, double endAngle)
: Interpolator(AngleInterpolator::GetRotateDuration(startAngle, endAngle), delay)
, m_startAngle(ang::AngleIn2PI(startAngle))
, m_endAngle(ang::AngleIn2PI(endAngle))
, m_angle(m_startAngle)
{
SetActive((GetDuration() > 0.0) && (m_startAngle != m_endAngle));
}
AngleInterpolator::AngleInterpolator(double delay, double duration, double startAngle, double endAngle)
: Interpolator(duration, delay)
, m_startAngle(ang::AngleIn2PI(startAngle))
, m_endAngle(ang::AngleIn2PI(endAngle))
, m_angle(m_startAngle)
{
SetActive((GetDuration() > 0.0) && (m_startAngle != m_endAngle));
}
// static
double AngleInterpolator::GetRotateDuration(double startAngle, double endAngle)
{
double constexpr kRotateDurationScalar = 0.75;
startAngle = ang::AngleIn2PI(startAngle);
endAngle = ang::AngleIn2PI(endAngle);
return kRotateDurationScalar * fabs(ang::GetShortestDistance(startAngle, endAngle)) / math::pi;
}
void AngleInterpolator::Advance(double elapsedSeconds)
{
TBase::Advance(elapsedSeconds);
m_angle = m_startAngle + ang::GetShortestDistance(m_startAngle, m_endAngle) * GetT();
}
void AngleInterpolator::Finish()
{
TBase::Finish();
m_angle = m_endAngle;
}
} // namespace df

View file

@ -0,0 +1,135 @@
#pragma once
#include "geometry/screenbase.hpp"
namespace df
{
class Interpolator
{
public:
Interpolator(double duration, double delay = 0);
virtual ~Interpolator() = default;
virtual void Advance(double elapsedSeconds);
virtual void Finish();
bool IsActive() const;
void SetActive(bool active);
bool IsFinished() const;
static double constexpr kInvalidDuration = -1.0;
void SetMaxDuration(double maxDuration);
void SetMinDuration(double minDuration);
double GetMaxDuration() const;
double GetMinDuration() const;
double GetDuration() const;
protected:
double GetT() const;
double GetElapsedTime() const;
private:
double m_elapsedTime;
double m_duration;
double m_maxDuration;
double m_minDuration;
double m_delay;
bool m_isActive;
};
class PositionInterpolator : public Interpolator
{
using TBase = Interpolator;
public:
PositionInterpolator();
PositionInterpolator(double duration, double delay, m2::PointD const & startPosition, m2::PointD const & endPosition);
PositionInterpolator(m2::PointD const & startPosition, m2::PointD const & endPosition, ScreenBase const & convertor);
PositionInterpolator(double delay, m2::PointD const & startPosition, m2::PointD const & endPosition,
ScreenBase const & convertor);
PositionInterpolator(m2::PointD const & startPosition, m2::PointD const & endPosition, m2::RectD const & viewportRect,
double scale);
PositionInterpolator(double delay, m2::PointD const & startPosition, m2::PointD const & endPosition,
m2::RectD const & viewportRect, double scale);
static double GetMoveDuration(double globalDistance, m2::RectD const & viewportRect, double scale);
static double GetMoveDuration(m2::PointD const & startPosition, m2::PointD const & endPosition,
m2::RectD const & viewportRect, double scale);
static double GetMoveDuration(m2::PointD const & startPosition, m2::PointD const & endPosition,
ScreenBase const & convertor);
// Interpolator overrides:
void Advance(double elapsedSeconds) override;
void Finish() override;
m2::PointD GetPosition() const { return m_position; }
m2::PointD GetTargetPosition() const { return m_endPosition; }
private:
m2::PointD m_startPosition;
m2::PointD m_endPosition;
m2::PointD m_position;
};
class ScaleInterpolator : public Interpolator
{
using TBase = Interpolator;
public:
ScaleInterpolator();
ScaleInterpolator(double startScale, double endScale, bool isAutoZoom);
ScaleInterpolator(double delay, double startScale, double endScale, bool isAutoZoom);
static double GetScaleDuration(double startScale, double endScale, bool isAutoZoom);
// Interpolator overrides:
void Advance(double elapsedSeconds) override;
void Finish() override;
double GetScale() const { return m_scale; }
double GetStartScale() const { return m_startScale; }
double GetTargetScale() const { return m_endScale; }
private:
double m_startScale;
double m_endScale;
double m_scale;
};
class AngleInterpolator : public Interpolator
{
using TBase = Interpolator;
public:
AngleInterpolator();
AngleInterpolator(double startAngle, double endAngle);
AngleInterpolator(double delay, double startAngle, double endAngle);
AngleInterpolator(double delay, double duration, double startAngle, double endAngle);
static double GetRotateDuration(double startAngle, double endAngle);
// Interpolator overrides:
void Advance(double elapsedSeconds) override;
void Finish() override;
double GetAngle() const { return m_angle; }
double GetStartAngle() const { return m_startAngle; }
double GetTargetAngle() const { return m_endAngle; }
private:
double m_startAngle;
double m_endAngle;
double m_angle;
};
} // namespace df

View file

@ -0,0 +1,243 @@
#include "drape_frontend/animation/linear_animation.hpp"
#include "base/assert.hpp"
#include <algorithm>
namespace df
{
MapLinearAnimation::MapLinearAnimation(m2::PointD const & startPos, m2::PointD const & endPos, double startAngle,
double endAngle, double startScale, double endScale,
ScreenBase const & convertor)
: Animation(true /* couldBeInterrupted */, false /* couldBeBlended */)
, m_angleInterpolator(startAngle, endAngle)
, m_positionInterpolator(startPos, endPos, convertor)
, m_scaleInterpolator(startScale, endScale, false /* isAutoZoom */)
{
m_objects.insert(Animation::Object::MapPlane);
if (m_positionInterpolator.IsActive())
m_properties.insert(Animation::ObjectProperty::Position);
if (m_angleInterpolator.IsActive())
m_properties.insert(Animation::ObjectProperty::Angle);
if (m_scaleInterpolator.IsActive())
m_properties.insert(Animation::ObjectProperty::Scale);
}
MapLinearAnimation::MapLinearAnimation() : Animation(true /* couldBeInterrupted */, false /* couldBeBlended */)
{
m_objects.insert(Animation::Object::MapPlane);
}
void MapLinearAnimation::Init(ScreenBase const & screen, TPropertyCache const & properties)
{
ScreenBase currentScreen;
GetCurrentScreen(properties, screen, currentScreen);
double minDuration = m_positionInterpolator.GetMinDuration();
double maxDuration = m_positionInterpolator.GetMaxDuration();
SetMove(currentScreen.GlobalRect().GlobalZero(), m_positionInterpolator.GetTargetPosition(), currentScreen);
m_positionInterpolator.SetMinDuration(minDuration);
m_positionInterpolator.SetMaxDuration(maxDuration);
minDuration = m_scaleInterpolator.GetMinDuration();
maxDuration = m_scaleInterpolator.GetMaxDuration();
SetScale(currentScreen.GetScale(), m_scaleInterpolator.GetTargetScale());
m_scaleInterpolator.SetMinDuration(minDuration);
m_scaleInterpolator.SetMaxDuration(maxDuration);
minDuration = m_angleInterpolator.GetMinDuration();
maxDuration = m_angleInterpolator.GetMaxDuration();
SetRotate(currentScreen.GetAngle(), m_angleInterpolator.GetTargetAngle());
m_angleInterpolator.SetMinDuration(minDuration);
m_angleInterpolator.SetMaxDuration(maxDuration);
}
void MapLinearAnimation::SetMove(m2::PointD const & startPos, m2::PointD const & endPos, ScreenBase const & convertor)
{
m_positionInterpolator = PositionInterpolator(startPos, endPos, convertor);
if (m_positionInterpolator.IsActive())
m_properties.insert(Animation::ObjectProperty::Position);
}
void MapLinearAnimation::SetMove(m2::PointD const & startPos, m2::PointD const & endPos, m2::RectD const & viewportRect,
double scale)
{
m_positionInterpolator = PositionInterpolator(startPos, endPos, viewportRect, scale);
if (m_positionInterpolator.IsActive())
m_properties.insert(Animation::ObjectProperty::Position);
}
void MapLinearAnimation::SetRotate(double startAngle, double endAngle)
{
m_angleInterpolator = AngleInterpolator(startAngle, endAngle);
if (m_angleInterpolator.IsActive())
m_properties.insert(Animation::ObjectProperty::Angle);
}
void MapLinearAnimation::SetScale(double startScale, double endScale)
{
m_scaleInterpolator = ScaleInterpolator(startScale, endScale, false /* isAutoZoom */);
if (m_scaleInterpolator.IsActive())
m_properties.insert(Animation::ObjectProperty::Scale);
}
Animation::TObjectProperties const & MapLinearAnimation::GetProperties(Object object) const
{
ASSERT_EQUAL(static_cast<int>(object), static_cast<int>(Animation::Object::MapPlane), ());
return m_properties;
}
bool MapLinearAnimation::HasProperty(Object object, ObjectProperty property) const
{
return HasObject(object) && m_properties.find(property) != m_properties.end();
}
void MapLinearAnimation::Advance(double elapsedSeconds)
{
if (m_angleInterpolator.IsActive())
m_angleInterpolator.Advance(elapsedSeconds);
if (m_scaleInterpolator.IsActive())
m_scaleInterpolator.Advance(elapsedSeconds);
if (m_positionInterpolator.IsActive())
m_positionInterpolator.Advance(elapsedSeconds);
}
void MapLinearAnimation::Finish()
{
if (m_angleInterpolator.IsActive())
m_angleInterpolator.Finish();
if (m_scaleInterpolator.IsActive())
m_scaleInterpolator.Finish();
if (m_positionInterpolator.IsActive())
m_positionInterpolator.Finish();
Animation::Finish();
}
void MapLinearAnimation::SetMaxDuration(double maxDuration)
{
if (m_angleInterpolator.IsActive())
m_angleInterpolator.SetMaxDuration(maxDuration);
if (m_positionInterpolator.IsActive())
m_positionInterpolator.SetMaxDuration(maxDuration);
SetMaxScaleDuration(maxDuration);
}
void MapLinearAnimation::SetMinDuration(double minDuration)
{
if (m_angleInterpolator.IsActive())
m_angleInterpolator.SetMinDuration(minDuration);
if (m_positionInterpolator.IsActive())
m_positionInterpolator.SetMinDuration(minDuration);
if (m_scaleInterpolator.IsActive())
m_scaleInterpolator.SetMinDuration(minDuration);
}
void MapLinearAnimation::SetMaxScaleDuration(double maxDuration)
{
if (m_scaleInterpolator.IsActive())
m_scaleInterpolator.SetMaxDuration(maxDuration);
}
double MapLinearAnimation::GetDuration() const
{
double duration = 0.0;
if (m_angleInterpolator.IsActive())
duration = m_angleInterpolator.GetDuration();
if (m_scaleInterpolator.IsActive())
duration = std::max(duration, m_scaleInterpolator.GetDuration());
if (m_positionInterpolator.IsActive())
duration = std::max(duration, m_positionInterpolator.GetDuration());
return duration;
}
double MapLinearAnimation::GetMaxDuration() const
{
double maxDuration = Animation::kInvalidAnimationDuration;
if (!Animation::GetMaxDuration(m_angleInterpolator, maxDuration) ||
!Animation::GetMaxDuration(m_scaleInterpolator, maxDuration) ||
!Animation::GetMaxDuration(m_positionInterpolator, maxDuration))
return Animation::kInvalidAnimationDuration;
return maxDuration;
}
double MapLinearAnimation::GetMinDuration() const
{
double minDuration = Animation::kInvalidAnimationDuration;
if (!Animation::GetMinDuration(m_angleInterpolator, minDuration) ||
!Animation::GetMinDuration(m_scaleInterpolator, minDuration) ||
!Animation::GetMinDuration(m_positionInterpolator, minDuration))
return Animation::kInvalidAnimationDuration;
return minDuration;
}
bool MapLinearAnimation::IsFinished() const
{
return m_angleInterpolator.IsFinished() && m_scaleInterpolator.IsFinished() && m_positionInterpolator.IsFinished();
}
bool MapLinearAnimation::GetProperty(Object object, ObjectProperty property, PropertyValue & value) const
{
return GetProperty(object, property, false /* targetValue */, value);
}
bool MapLinearAnimation::GetTargetProperty(Object object, ObjectProperty property, PropertyValue & value) const
{
return GetProperty(object, property, true /* targetValue */, value);
}
bool MapLinearAnimation::GetProperty(Object object, ObjectProperty property, bool targetValue,
PropertyValue & value) const
{
ASSERT_EQUAL(static_cast<int>(object), static_cast<int>(Animation::Object::MapPlane), ());
switch (property)
{
case Animation::ObjectProperty::Position:
if (m_positionInterpolator.IsActive())
{
value = PropertyValue(targetValue ? m_positionInterpolator.GetTargetPosition()
: m_positionInterpolator.GetPosition());
return true;
}
return false;
case Animation::ObjectProperty::Scale:
if (m_scaleInterpolator.IsActive())
{
value = PropertyValue(targetValue ? m_scaleInterpolator.GetTargetScale() : m_scaleInterpolator.GetScale());
return true;
}
return false;
case Animation::ObjectProperty::Angle:
if (m_angleInterpolator.IsActive())
{
value = PropertyValue(targetValue ? m_angleInterpolator.GetTargetAngle() : m_angleInterpolator.GetAngle());
return true;
}
return false;
default: ASSERT(false, ("Wrong property:", static_cast<int>(property)));
}
return false;
}
} // namespace df

View file

@ -0,0 +1,55 @@
#pragma once
#include "animation.hpp"
#include "interpolators.hpp"
namespace df
{
class MapLinearAnimation : public Animation
{
public:
MapLinearAnimation(m2::PointD const & startPos, m2::PointD const & endPos, double startAngle, double endAngle,
double startScale, double endScale, ScreenBase const & convertor);
MapLinearAnimation();
void Init(ScreenBase const & screen, TPropertyCache const & properties) override;
void SetMove(m2::PointD const & startPos, m2::PointD const & endPos, ScreenBase const & convertor);
void SetMove(m2::PointD const & startPos, m2::PointD const & endPos, m2::RectD const & viewportRect, double scale);
void SetRotate(double startAngle, double endAngle);
void SetScale(double startScale, double endScale);
Animation::Type GetType() const override { return Animation::Type::MapLinear; }
TAnimObjects const & GetObjects() const override { return m_objects; }
bool HasObject(Object object) const override { return object == Animation::Object::MapPlane; }
TObjectProperties const & GetProperties(Object object) const override;
bool HasProperty(Object object, ObjectProperty property) const override;
void Advance(double elapsedSeconds) override;
void Finish() override;
void SetMaxDuration(double maxDuration) override;
void SetMinDuration(double minDuration) override;
double GetDuration() const override;
double GetMaxDuration() const override;
double GetMinDuration() const override;
bool IsFinished() const override;
bool GetProperty(Object object, ObjectProperty property, PropertyValue & value) const override;
bool GetTargetProperty(Object object, ObjectProperty property, PropertyValue & value) const override;
void SetMaxScaleDuration(double maxDuration);
private:
bool GetProperty(Object object, ObjectProperty property, bool targetValue, PropertyValue & value) const;
AngleInterpolator m_angleInterpolator;
PositionInterpolator m_positionInterpolator;
ScaleInterpolator m_scaleInterpolator;
TObjectProperties m_properties;
TAnimObjects m_objects;
};
} // namespace df

View file

@ -0,0 +1,24 @@
#include "drape_frontend/animation/opacity_animation.hpp"
#include "drape_frontend/animation/interpolations.hpp"
namespace df
{
OpacityAnimation::OpacityAnimation(double duration, double startOpacity, double endOpacity)
: OpacityAnimation(duration, 0.0, startOpacity, endOpacity)
{}
OpacityAnimation::OpacityAnimation(double duration, double delay, double startOpacity, double endOpacity)
: BaseInterpolator(duration, delay)
, m_startOpacity(startOpacity)
, m_endOpacity(endOpacity)
, m_opacity(startOpacity)
{}
void OpacityAnimation::Advance(double elapsedSeconds)
{
TBase::Advance(elapsedSeconds);
m_opacity = InterpolateDouble(m_startOpacity, m_endOpacity, GetT());
}
} // namespace df

View file

@ -0,0 +1,23 @@
#pragma once
#include "drape_frontend/animation/base_interpolator.hpp"
namespace df
{
class OpacityAnimation : public BaseInterpolator
{
using TBase = BaseInterpolator;
public:
OpacityAnimation(double duration, double startOpacity, double endOpacity);
OpacityAnimation(double duration, double delay, double startOpacity, double endOpacity);
void Advance(double elapsedSeconds) override;
double GetOpacity() const { return m_opacity; }
private:
double m_startOpacity;
double m_endOpacity;
double m_opacity;
};
} // namespace df

View file

@ -0,0 +1,206 @@
#include "drape_frontend/animation/parallel_animation.hpp"
#include "drape_frontend/animation_system.hpp"
#include <algorithm>
namespace df
{
ParallelAnimation::ParallelAnimation() : Animation(true /* couldBeInterrupted */, true /* couldBeBlended */) {}
void ParallelAnimation::Init(ScreenBase const & screen, TPropertyCache const & properties)
{
for (auto const & anim : m_animations)
anim->Init(screen, properties);
}
std::string ParallelAnimation::GetCustomType() const
{
return m_customType;
}
void ParallelAnimation::SetCustomType(std::string const & type)
{
m_customType = type;
}
Animation::TAnimObjects const & ParallelAnimation::GetObjects() const
{
return m_objects;
}
bool ParallelAnimation::HasObject(Object object) const
{
return m_objects.find(object) != m_objects.end();
}
Animation::TObjectProperties const & ParallelAnimation::GetProperties(Object object) const
{
ASSERT(HasObject(object), ());
return m_properties.find(object)->second;
}
bool ParallelAnimation::HasProperty(Object object, ObjectProperty property) const
{
if (!HasObject(object))
return false;
TObjectProperties const & properties = GetProperties(object);
return properties.find(property) != properties.end();
}
bool ParallelAnimation::HasTargetProperty(Object object, ObjectProperty property) const
{
ASSERT(!m_animations.empty(), ());
for (auto const & anim : m_animations)
if (anim->HasTargetProperty(object, property))
return true;
return false;
}
void ParallelAnimation::SetMaxDuration(double maxDuration)
{
for (auto const & anim : m_animations)
anim->SetMaxDuration(maxDuration);
}
void ParallelAnimation::SetMinDuration(double minDuration)
{
for (auto const & anim : m_animations)
anim->SetMinDuration(minDuration);
}
double ParallelAnimation::GetDuration() const
{
double duration = 0.0;
for (auto const & anim : m_animations)
duration = std::max(duration, anim->GetDuration());
return duration;
}
double ParallelAnimation::GetMaxDuration() const
{
double maxDuration = Animation::kInvalidAnimationDuration;
double duration;
for (auto const & anim : m_animations)
{
duration = anim->GetMaxDuration();
if (duration < 0.0)
return Animation::kInvalidAnimationDuration;
maxDuration = maxDuration >= 0 ? std::max(duration, maxDuration) : duration;
}
return maxDuration;
}
double ParallelAnimation::GetMinDuration() const
{
double minDuration = Animation::kInvalidAnimationDuration;
double duration;
for (auto const & anim : m_animations)
{
duration = anim->GetMinDuration();
if (duration < 0.0)
return Animation::kInvalidAnimationDuration;
minDuration = minDuration >= 0 ? std::min(duration, minDuration) : duration;
}
return minDuration;
}
bool ParallelAnimation::IsFinished() const
{
return m_animations.empty();
}
bool ParallelAnimation::GetProperty(Object object, ObjectProperty property, PropertyValue & value) const
{
ASSERT(!m_animations.empty(), ());
for (auto const & anim : m_animations)
if (anim->HasProperty(object, property))
return anim->GetProperty(object, property, value);
return false;
}
bool ParallelAnimation::GetTargetProperty(Object object, ObjectProperty property, PropertyValue & value) const
{
ASSERT(!m_animations.empty(), ());
for (auto const & anim : m_animations)
if (anim->HasProperty(object, property))
return anim->GetTargetProperty(object, property, value);
return false;
}
void ParallelAnimation::AddAnimation(drape_ptr<Animation> && animation)
{
SetCouldBeInterrupted(CouldBeInterrupted() && animation->CouldBeInterrupted());
SetCouldBeBlended(CouldBeBlended() && animation->CouldBeBlended());
SetCouldBeRewinded(CouldBeRewinded() && animation->CouldBeRewinded());
m_animations.emplace_back(std::move(animation));
ObtainObjectProperties();
}
void ParallelAnimation::OnStart()
{
for (auto & anim : m_animations)
anim->OnStart();
}
void ParallelAnimation::OnFinish()
{
for (auto & anim : m_animations)
anim->OnFinish();
}
void ParallelAnimation::Advance(double elapsedSeconds)
{
auto iter = m_animations.begin();
while (iter != m_animations.end())
{
(*iter)->Advance(elapsedSeconds);
if ((*iter)->IsFinished())
{
(*iter)->OnFinish();
AnimationSystem::Instance().SaveAnimationResult(*(*iter));
iter = m_animations.erase(iter);
ObtainObjectProperties();
}
else
{
++iter;
}
}
}
void ParallelAnimation::Finish()
{
for (auto & anim : m_animations)
{
anim->Finish();
AnimationSystem::Instance().SaveAnimationResult(*anim);
}
m_animations.clear();
ObtainObjectProperties();
Animation::Finish();
}
void ParallelAnimation::ObtainObjectProperties()
{
m_objects.clear();
m_properties.clear();
if (m_animations.empty())
return;
for (auto const & anim : m_animations)
{
TAnimObjects const & objects = anim->GetObjects();
m_objects.insert(objects.begin(), objects.end());
for (auto const & object : objects)
{
TObjectProperties const & properties = anim->GetProperties(object);
m_properties[object].insert(properties.begin(), properties.end());
}
}
}
} // namespace df

View file

@ -0,0 +1,74 @@
#pragma once
#include "animation.hpp"
#include "drape/pointers.hpp"
#include <list>
#include <string>
namespace df
{
class ParallelAnimation : public Animation
{
public:
ParallelAnimation();
void Init(ScreenBase const & screen, TPropertyCache const & properties) override;
Animation::Type GetType() const override { return Animation::Type::Parallel; }
TAnimObjects const & GetObjects() const override;
bool HasObject(Object object) const override;
TObjectProperties const & GetProperties(Object object) const override;
bool HasProperty(Object object, ObjectProperty property) const override;
bool HasTargetProperty(Object object, ObjectProperty property) const override;
std::string GetCustomType() const override;
void SetCustomType(std::string const & type);
void AddAnimation(drape_ptr<Animation> && animation);
void OnStart() override;
void OnFinish() override;
void SetMaxDuration(double maxDuration) override;
void SetMinDuration(double minDuration) override;
double GetDuration() const override;
double GetMaxDuration() const override;
double GetMinDuration() const override;
bool IsFinished() const override;
void Advance(double elapsedSeconds) override;
void Finish() override;
bool GetProperty(Object object, ObjectProperty property, PropertyValue & value) const override;
bool GetTargetProperty(Object object, ObjectProperty property, PropertyValue & value) const override;
template <typename T>
T const * FindAnimation(Animation::Type type, char const * customType = nullptr) const
{
for (auto const & anim : m_animations)
{
if ((anim->GetType() == type) && (customType == nullptr || anim->GetCustomType() == customType))
{
ASSERT(dynamic_cast<T const *>(anim.get()) != nullptr, ());
return static_cast<T const *>(anim.get());
}
}
return nullptr;
}
private:
void ObtainObjectProperties();
std::list<drape_ptr<Animation>> m_animations;
TAnimObjects m_objects;
std::map<Object, TObjectProperties> m_properties;
std::string m_customType;
};
} // namespace df

View file

@ -0,0 +1,121 @@
#include "scale_animation.hpp"
#include "drape_frontend/animation_system.hpp"
#include "base/assert.hpp"
namespace df
{
MapScaleAnimation::MapScaleAnimation(double startScale, double endScale, m2::PointD const & globalScaleCenter,
m2::PointD const & pxScaleCenter)
: Animation(true /* couldBeInterrupted */, true /* couldBeBlended */)
, m_scaleInterpolator(startScale, endScale, false /* isAutoZoom */)
, m_pxScaleCenter(pxScaleCenter)
, m_globalScaleCenter(globalScaleCenter)
{
m_objects.insert(Animation::Object::MapPlane);
m_properties.insert(Animation::ObjectProperty::Scale);
m_properties.insert(Animation::ObjectProperty::Position);
}
void MapScaleAnimation::Init(ScreenBase const & screen, TPropertyCache const & properties)
{
ScreenBase currentScreen;
GetCurrentScreen(properties, screen, currentScreen);
double const minDuration = m_scaleInterpolator.GetMinDuration();
double const maxDuration = m_scaleInterpolator.GetMaxDuration();
m_scaleInterpolator =
ScaleInterpolator(currentScreen.GetScale(), m_scaleInterpolator.GetTargetScale(), false /* isAutoZoom */);
m_scaleInterpolator.SetMinDuration(minDuration);
m_scaleInterpolator.SetMaxDuration(maxDuration);
}
Animation::TObjectProperties const & MapScaleAnimation::GetProperties(Object object) const
{
ASSERT_EQUAL(static_cast<int>(object), static_cast<int>(Animation::Object::MapPlane), ());
return m_properties;
}
bool MapScaleAnimation::HasProperty(Object object, ObjectProperty property) const
{
return HasObject(object) && m_properties.find(property) != m_properties.end();
}
void MapScaleAnimation::Advance(double elapsedSeconds)
{
m_scaleInterpolator.Advance(elapsedSeconds);
}
void MapScaleAnimation::Finish()
{
m_scaleInterpolator.Finish();
Animation::Finish();
}
void MapScaleAnimation::SetMaxDuration(double maxDuration)
{
m_scaleInterpolator.SetMaxDuration(maxDuration);
}
void MapScaleAnimation::SetMinDuration(double minDuration)
{
m_scaleInterpolator.SetMinDuration(minDuration);
}
double MapScaleAnimation::GetDuration() const
{
return m_scaleInterpolator.GetDuration();
}
double MapScaleAnimation::GetMaxDuration() const
{
return m_scaleInterpolator.GetMaxDuration();
}
double MapScaleAnimation::GetMinDuration() const
{
return m_scaleInterpolator.GetMinDuration();
}
bool MapScaleAnimation::IsFinished() const
{
return m_scaleInterpolator.IsFinished();
}
bool MapScaleAnimation::GetProperty(Object object, ObjectProperty property, bool targetValue,
PropertyValue & value) const
{
ASSERT_EQUAL(static_cast<int>(object), static_cast<int>(Animation::Object::MapPlane), ());
if (property == Animation::ObjectProperty::Position)
{
ScreenBase screen = AnimationSystem::Instance().GetLastScreen();
screen.SetScale(targetValue ? m_scaleInterpolator.GetTargetScale() : m_scaleInterpolator.GetScale());
m2::PointD const pixelOffset = screen.PixelRect().Center() - screen.P3dtoP(m_pxScaleCenter);
value = PropertyValue(screen.PtoG(screen.GtoP(m_globalScaleCenter) + pixelOffset));
return true;
}
if (property == Animation::ObjectProperty::Scale)
{
value = PropertyValue(targetValue ? m_scaleInterpolator.GetTargetScale() : m_scaleInterpolator.GetScale());
return true;
}
ASSERT(false, ("Wrong property:", static_cast<int>(property)));
return false;
}
bool MapScaleAnimation::GetTargetProperty(Object object, ObjectProperty property, PropertyValue & value) const
{
return GetProperty(object, property, true /* targetValue */, value);
}
bool MapScaleAnimation::GetProperty(Object object, ObjectProperty property, PropertyValue & value) const
{
return GetProperty(object, property, false /* targetValue */, value);
}
} // namespace df

View file

@ -0,0 +1,49 @@
#pragma once
#include "animation.hpp"
#include "interpolators.hpp"
namespace df
{
class MapScaleAnimation : public Animation
{
public:
MapScaleAnimation(double startScale, double endScale, m2::PointD const & globalScaleCenter,
m2::PointD const & pxScaleCenter);
void Init(ScreenBase const & screen, TPropertyCache const & properties) override;
Animation::Type GetType() const override { return Animation::Type::MapScale; }
TAnimObjects const & GetObjects() const override { return m_objects; }
bool HasObject(Object object) const override { return object == Animation::Object::MapPlane; }
TObjectProperties const & GetProperties(Object object) const override;
bool HasProperty(Object object, ObjectProperty property) const override;
void Advance(double elapsedSeconds) override;
void Finish() override;
void SetMaxDuration(double maxDuration) override;
void SetMinDuration(double minDuration) override;
double GetDuration() const override;
double GetMaxDuration() const override;
double GetMinDuration() const override;
bool IsFinished() const override;
bool GetProperty(Object object, ObjectProperty property, PropertyValue & value) const override;
bool GetTargetProperty(Object object, ObjectProperty property, PropertyValue & value) const override;
private:
bool GetProperty(Object object, ObjectProperty property, bool targetValue, PropertyValue & value) const;
ScaleInterpolator m_scaleInterpolator;
m2::PointD const m_pxScaleCenter;
m2::PointD const m_globalScaleCenter;
TObjectProperties m_properties;
TAnimObjects m_objects;
};
} // namespace df

View file

@ -0,0 +1,196 @@
#include "sequence_animation.hpp"
#include "drape_frontend/animation_system.hpp"
#include "base/assert.hpp"
namespace df
{
SequenceAnimation::SequenceAnimation() : Animation(true /* couldBeInterrupted */, true /* couldBeBlended */) {}
void SequenceAnimation::Init(ScreenBase const & screen, TPropertyCache const & properties)
{
if (!m_animations.empty())
m_animations.front()->Init(screen, properties);
}
std::string SequenceAnimation::GetCustomType() const
{
return m_customType;
}
void SequenceAnimation::SetCustomType(std::string const & type)
{
m_customType = type;
}
Animation::TAnimObjects const & SequenceAnimation::GetObjects() const
{
return m_objects;
}
bool SequenceAnimation::HasObject(Object object) const
{
ASSERT(!m_animations.empty(), ());
return m_animations.front()->HasObject(object);
}
Animation::TObjectProperties const & SequenceAnimation::GetProperties(Object object) const
{
ASSERT(HasObject(object), ());
return m_properties.find(object)->second;
}
bool SequenceAnimation::HasProperty(Object object, ObjectProperty property) const
{
if (!HasObject(object))
return false;
ASSERT(!m_animations.empty(), ());
return m_animations.front()->HasProperty(object, property);
}
bool SequenceAnimation::HasTargetProperty(Object object, ObjectProperty property) const
{
ASSERT(!m_animations.empty(), ());
for (auto const & anim : m_animations)
if (anim->HasTargetProperty(object, property))
return true;
return false;
}
void SequenceAnimation::SetMaxDuration(double maxDuration)
{
ASSERT(false, ("Not implemented"));
}
void SequenceAnimation::SetMinDuration(double minDuration)
{
ASSERT(false, ("Not implemented"));
}
double SequenceAnimation::GetDuration() const
{
double duration = 0.0;
for (auto const & anim : m_animations)
duration += anim->GetDuration();
return duration;
}
double SequenceAnimation::GetMaxDuration() const
{
double maxDuration = 0.0;
double duration;
for (auto const & anim : m_animations)
{
duration = anim->GetMaxDuration();
if (duration < 0.0)
return Animation::kInvalidAnimationDuration;
maxDuration += duration;
}
return maxDuration;
}
double SequenceAnimation::GetMinDuration() const
{
double minDuration = 0.0;
double duration;
for (auto const & anim : m_animations)
{
duration = anim->GetMinDuration();
if (duration < 0.0)
return Animation::kInvalidAnimationDuration;
minDuration += duration;
}
return minDuration;
}
bool SequenceAnimation::IsFinished() const
{
return m_animations.empty();
}
bool SequenceAnimation::GetProperty(Object object, ObjectProperty property, PropertyValue & value) const
{
ASSERT(!m_animations.empty(), ());
return m_animations.front()->GetProperty(object, property, value);
}
bool SequenceAnimation::GetTargetProperty(Object object, ObjectProperty property, PropertyValue & value) const
{
ASSERT(!m_animations.empty(), ());
for (auto it = m_animations.rbegin(); it != m_animations.rend(); ++it)
{
auto const & anim = *it;
if (anim->HasTargetProperty(object, property))
return anim->GetTargetProperty(object, property, value);
}
return false;
}
void SequenceAnimation::AddAnimation(drape_ptr<Animation> && animation)
{
m_animations.emplace_back(std::move(animation));
if (m_animations.size() == 1)
ObtainObjectProperties();
}
void SequenceAnimation::OnStart()
{
if (m_animations.empty())
return;
m_animations.front()->OnStart();
Animation::OnStart();
}
void SequenceAnimation::OnFinish()
{
Animation::OnFinish();
}
void SequenceAnimation::Advance(double elapsedSeconds)
{
if (m_animations.empty())
return;
m_animations.front()->Advance(elapsedSeconds);
if (m_animations.front()->IsFinished())
{
m_animations.front()->OnFinish();
AnimationSystem::Instance().SaveAnimationResult(*m_animations.front());
m_animations.pop_front();
ObtainObjectProperties();
}
}
void SequenceAnimation::Finish()
{
for (auto & anim : m_animations)
{
anim->Finish();
AnimationSystem::Instance().SaveAnimationResult(*anim);
}
m_animations.clear();
ObtainObjectProperties();
Animation::Finish();
}
void SequenceAnimation::ObtainObjectProperties()
{
m_objects.clear();
m_properties.clear();
if (m_animations.empty())
return;
TAnimObjects const & objects = m_animations.front()->GetObjects();
m_objects.insert(objects.begin(), objects.end());
for (auto const & object : objects)
{
TObjectProperties const & properties = m_animations.front()->GetProperties(object);
m_properties[object].insert(properties.begin(), properties.end());
}
}
} // namespace df

View file

@ -0,0 +1,59 @@
#pragma once
#include "animation.hpp"
#include "drape/pointers.hpp"
#include <deque>
#include <map>
#include <string>
namespace df
{
class SequenceAnimation : public Animation
{
public:
SequenceAnimation();
void Init(ScreenBase const & screen, TPropertyCache const & properties) override;
Animation::Type GetType() const override { return Animation::Type::Sequence; }
TAnimObjects const & GetObjects() const override;
bool HasObject(Object object) const override;
TObjectProperties const & GetProperties(Object object) const override;
bool HasProperty(Object object, ObjectProperty property) const override;
bool HasTargetProperty(Object object, ObjectProperty property) const override;
std::string GetCustomType() const override;
void SetCustomType(std::string const & type);
void SetMaxDuration(double maxDuration) override;
void SetMinDuration(double minDuration) override;
double GetDuration() const override;
double GetMaxDuration() const override;
double GetMinDuration() const override;
bool IsFinished() const override;
bool GetProperty(Object object, ObjectProperty property, PropertyValue & value) const override;
bool GetTargetProperty(Object object, ObjectProperty property, PropertyValue & value) const override;
void AddAnimation(drape_ptr<Animation> && animation);
void OnStart() override;
void OnFinish() override;
void Advance(double elapsedSeconds) override;
void Finish() override;
private:
void ObtainObjectProperties();
std::deque<drape_ptr<Animation>> m_animations;
TAnimObjects m_objects;
std::map<Object, TObjectProperties> m_properties;
std::string m_customType;
};
} // namespace df

View file

@ -0,0 +1,108 @@
#include "show_hide_animation.hpp"
#include "base_interpolator.hpp"
#include "base/math.hpp"
namespace df
{
class ShowHideAnimation::ShowHideInterpolator : public BaseInterpolator
{
public:
ShowHideInterpolator(ShowHideAnimation::EState & state, double startT, double endT, double duration)
: BaseInterpolator(duration)
, m_state(state)
, m_startT(startT)
, m_endT(endT)
{
m_state = m_endT > m_startT ? ShowHideAnimation::STATE_SHOW_DIRECTION : ShowHideAnimation::STATE_HIDE_DIRECTION;
}
void Advance(double elapsedSeconds) override
{
BaseInterpolator::Advance(elapsedSeconds);
if (IsFinished())
m_state = m_endT > m_startT ? ShowHideAnimation::STATE_VISIBLE : ShowHideAnimation::STATE_INVISIBLE;
}
double GetCurrentT() const { return m_startT + (m_endT - m_startT) * GetT(); }
private:
ShowHideAnimation::EState & m_state;
double m_startT;
double m_endT;
};
ShowHideAnimation::ShowHideAnimation(bool isInitialVisible, double fullDuration)
: m_state(isInitialVisible ? STATE_VISIBLE : STATE_INVISIBLE)
, m_fullDuration(fullDuration)
{}
ShowHideAnimation::~ShowHideAnimation()
{
m_interpolator.reset();
}
void ShowHideAnimation::Show()
{
EState state = GetState();
if (state == STATE_INVISIBLE || state == STATE_HIDE_DIRECTION)
{
m_state = STATE_VISIBLE;
m_interpolator.reset();
}
}
void ShowHideAnimation::ShowAnimated()
{
RefreshInterpolator({{STATE_VISIBLE, STATE_SHOW_DIRECTION}}, 1.0);
}
void ShowHideAnimation::Hide()
{
EState state = GetState();
if (state == STATE_VISIBLE || state == STATE_SHOW_DIRECTION)
{
m_state = STATE_INVISIBLE;
m_interpolator.reset();
}
}
void ShowHideAnimation::HideAnimated()
{
RefreshInterpolator({{STATE_INVISIBLE, STATE_HIDE_DIRECTION}}, 0.0);
}
ShowHideAnimation::EState ShowHideAnimation::GetState() const
{
return m_state;
}
double ShowHideAnimation::GetT() const
{
if (m_interpolator)
return m_interpolator->GetCurrentT();
ASSERT(m_state != STATE_SHOW_DIRECTION, ());
ASSERT(m_state != STATE_HIDE_DIRECTION, ());
return m_state == STATE_VISIBLE ? 1.0 : 0.0;
}
bool ShowHideAnimation::IsFinished() const
{
return m_interpolator == nullptr || m_interpolator->IsFinished();
}
void ShowHideAnimation::RefreshInterpolator(std::array<EState, 2> validStates, double endValue)
{
EState state = GetState();
if (state == validStates[0] || state == validStates[1])
return;
double start = GetT();
double end = endValue;
double duration = fabs(end - start) * m_fullDuration;
m_interpolator.reset(new ShowHideInterpolator(m_state, start, end, duration));
}
} // namespace df

View file

@ -0,0 +1,39 @@
#pragma once
#include "drape/pointers.hpp"
#include <array>
namespace df
{
class ShowHideAnimation
{
public:
enum EState
{
STATE_INVISIBLE,
STATE_VISIBLE,
STATE_SHOW_DIRECTION,
STATE_HIDE_DIRECTION
};
ShowHideAnimation(bool isInitialVisible, double fullDuration);
~ShowHideAnimation();
void Show();
void ShowAnimated();
void Hide();
void HideAnimated();
EState GetState() const;
double GetT() const;
bool IsFinished() const;
private:
void RefreshInterpolator(std::array<EState, 2> validStates, double endValue);
class ShowHideInterpolator;
drape_ptr<ShowHideInterpolator> m_interpolator;
ShowHideAnimation::EState m_state;
double m_fullDuration;
};
} // namespace df

View file

@ -0,0 +1,75 @@
#pragma once
#include "base/buffer_vector.hpp"
#include "base/logging.hpp"
#include <utility>
namespace df
{
template <typename TValue>
class ValueMapping
{
/// double = interpolation point [0.0, 1.0]
/// TValue = output value
using TRangePoint = std::pair<double, TValue>;
using TRangeVector = buffer_vector<TRangePoint, 8>;
using TRangeIter = typename TRangeVector::const_iterator;
public:
ValueMapping() = default;
void AddRangePoint(double t, TValue const & v)
{
#ifdef DEBUG
if (!m_ranges.empty())
ASSERT(m_ranges.back().first < t, ());
#endif
m_ranges.emplace_back(t, v);
}
TValue GetValue(double t)
{
TRangePoint startPoint, endPoint;
GetRangePoints(t, startPoint, endPoint);
double normT = t - startPoint.first;
double rangeT = endPoint.first - startPoint.first;
double rangeV = endPoint.second - startPoint.second;
return startPoint.second + rangeV * normT / rangeT;
}
private:
void GetRangePoints(double t, TRangePoint & startPoint, TRangePoint & endPoint)
{
ASSERT(t >= 0.0 && t <= 1.0, ());
ASSERT(m_ranges.size() > 1, ());
TRangeIter startIter = m_ranges.begin();
if (t < startIter->first)
{
startPoint.first = 0.0;
startPoint.second = TValue();
endPoint = *startIter;
return;
}
TRangeIter endIter = startIter + 1;
while (t > endIter->first)
{
endIter++;
ASSERT(m_ranges.end() != endIter, ());
}
ASSERT(startIter->first <= t && t <= endIter->first, ());
startPoint = *startIter;
endPoint = *endIter;
}
private:
TRangeVector m_ranges;
};
} // namespace df

View file

@ -0,0 +1,7 @@
#pragma once
namespace df
{
double constexpr kMaxAnimationTimeSec = 1.5; // in seconds
} // namespace df

View file

@ -0,0 +1,491 @@
#include "animation_system.hpp"
#include "base/logging.hpp"
#include <vector>
using namespace std::placeholders;
namespace df
{
namespace
{
class PropertyBlender
{
public:
PropertyBlender() = default;
void Blend(Animation::PropertyValue const & value)
{
if (m_counter != 0)
{
// New value type resets current blended value.
if (m_value.m_type != value.m_type)
{
m_value = value;
m_counter = 1;
return;
}
if (value.m_type == Animation::PropertyValue::Type::ValueD)
m_value.m_valueD += value.m_valueD;
else if (value.m_type == Animation::PropertyValue::Type::ValuePointD)
m_value.m_valuePointD += value.m_valuePointD;
}
else
{
m_value = value;
}
m_counter++;
}
Animation::PropertyValue Finish()
{
if (m_counter == 0)
return m_value;
double const scalar = 1.0 / m_counter;
m_counter = 0;
if (m_value.m_type == Animation::PropertyValue::Type::ValueD)
m_value.m_valueD *= scalar;
else if (m_value.m_type == Animation::PropertyValue::Type::ValuePointD)
m_value.m_valuePointD *= scalar;
return m_value;
}
bool IsEmpty() const { return m_counter == 0; }
private:
Animation::PropertyValue m_value;
uint32_t m_counter = 0;
};
} // namespace
void AnimationSystem::UpdateLastScreen(ScreenBase const & currentScreen)
{
m_lastScreen = currentScreen;
}
bool AnimationSystem::GetScreen(ScreenBase const & currentScreen, ScreenBase & screen)
{
return GetScreen(currentScreen, std::bind(&AnimationSystem::GetProperty, this, _1, _2, _3), screen);
}
void AnimationSystem::GetTargetScreen(ScreenBase const & currentScreen, ScreenBase & screen)
{
GetScreen(currentScreen, std::bind(&AnimationSystem::GetTargetProperty, this, _1, _2, _3), screen);
}
bool AnimationSystem::GetScreen(ScreenBase const & currentScreen, TGetPropertyFn const & getPropertyFn,
ScreenBase & screen)
{
ASSERT(getPropertyFn != nullptr, ());
m_lastScreen = currentScreen;
double scale = currentScreen.GetScale();
double angle = currentScreen.GetAngle();
m2::PointD pos = currentScreen.GlobalRect().GlobalZero();
Animation::PropertyValue value;
if (getPropertyFn(Animation::Object::MapPlane, Animation::ObjectProperty::Scale, value))
scale = value.m_valueD;
if (getPropertyFn(Animation::Object::MapPlane, Animation::ObjectProperty::Angle, value))
angle = value.m_valueD;
if (getPropertyFn(Animation::Object::MapPlane, Animation::ObjectProperty::Position, value))
pos = value.m_valuePointD;
screen = currentScreen;
screen.SetFromParams(pos, angle, scale);
return true;
}
bool AnimationSystem::GetArrowPosition(m2::PointD & position)
{
Animation::PropertyValue value;
if (GetProperty(Animation::Object::MyPositionArrow, Animation::ObjectProperty::Position, value))
{
position = value.m_valuePointD;
return true;
}
return false;
}
bool AnimationSystem::GetArrowAngle(double & angle)
{
Animation::PropertyValue value;
if (GetProperty(Animation::Object::MyPositionArrow, Animation::ObjectProperty::Angle, value))
{
angle = value.m_valueD;
return true;
}
return false;
}
bool AnimationSystem::AnimationExists(Animation::Object object) const
{
if (!m_animationChain.empty())
{
for (auto const & anim : *(m_animationChain.front()))
if (anim->HasObject(object))
return true;
}
for (auto const & prop : m_propertyCache)
if (prop.first.first == object)
return true;
return false;
}
bool AnimationSystem::HasAnimations() const
{
return !m_animationChain.empty();
}
bool AnimationSystem::HasMapAnimations() const
{
if (AnimationExists(Animation::Object::MapPlane))
return true;
if (AnimationExists(Animation::Object::Selection))
return true;
return false;
}
AnimationSystem & AnimationSystem::Instance()
{
static AnimationSystem animSystem;
return animSystem;
}
void AnimationSystem::CombineAnimation(drape_ptr<Animation> && animation)
{
#ifdef DEBUG_ANIMATIONS
LOG(LINFO, ("Combine animation", animation->GetType()));
#endif
TAnimationList interruptedAnimations;
bool startImmediately = true;
for (auto & pList : m_animationChain)
{
auto & lst = *pList;
bool couldBeBlended = animation->CouldBeBlended();
for (auto it = lst.begin(); it != lst.end();)
{
auto & anim = *it;
if (anim->GetInterruptedOnCombine() && anim->HasSameObjects(*animation))
{
#ifdef DEBUG_ANIMATIONS
LOG(LINFO, ("Interrupted on combine", anim->GetType()));
#endif
interruptedAnimations.splice(interruptedAnimations.end(), lst, it++);
}
else if (!anim->CouldBeBlendedWith(*animation))
{
if (!anim->CouldBeInterrupted())
{
couldBeBlended = false;
break;
}
#ifdef DEBUG_ANIMATIONS
LOG(LINFO, ("Couldn't be blended, interrupted", anim->GetType()));
#endif
interruptedAnimations.splice(interruptedAnimations.end(), lst, it++);
}
else
{
++it;
}
}
for (auto & anim : interruptedAnimations)
{
anim->Interrupt();
SaveAnimationResult(*anim);
}
if (couldBeBlended)
{
#ifdef DEBUG_ANIMATIONS
LOG(LINFO, ("Animation blended"));
#endif
lst.emplace_back(std::move(animation));
if (startImmediately)
lst.back()->OnStart();
#ifdef DEBUG_ANIMATIONS
Print();
#endif
return;
}
else if (m_animationChain.size() > 1 && animation->CouldBeInterrupted())
{
#ifdef DEBUG_ANIMATIONS
LOG(LINFO, ("Animation rejected"));
Print();
#endif
return;
}
startImmediately = false;
}
PushAnimation(std::move(animation));
}
void AnimationSystem::PushAnimation(drape_ptr<Animation> && animation)
{
#ifdef DEBUG_ANIMATIONS
LOG(LINFO, ("Push animation", animation->GetType()));
#endif
auto pList = std::make_shared<TAnimationList>();
pList->emplace_back(std::move(animation));
bool startImmediately = m_animationChain.empty();
m_animationChain.push_back(pList);
if (startImmediately)
pList->front()->OnStart();
#ifdef DEBUG_ANIMATIONS
Print();
#endif
}
void AnimationSystem::FinishAnimations(std::function<bool(std::shared_ptr<Animation> const &)> const & predicate,
bool rewind, bool finishAll)
{
if (m_animationChain.empty())
return;
#ifdef DEBUG_ANIMATIONS
bool changed = false;
#endif
TAnimationList & frontList = *(m_animationChain.front());
TAnimationList finishAnimations;
for (auto it = frontList.begin(); it != frontList.end();)
{
auto & anim = *it;
if (predicate(anim))
{
#ifdef DEBUG_ANIMATIONS
LOG(LINFO,
("Finish animation", anim->GetType(), ", rewind:", rewind, ", couldBeRewinded:", anim->CouldBeRewinded()));
changed = true;
#endif
finishAnimations.splice(finishAnimations.end(), frontList, it++);
}
else
{
++it;
}
}
if (finishAll)
{
for (auto & pList : m_animationChain)
{
auto & lst = *pList;
for (auto it = lst.begin(); it != lst.end();)
{
if (predicate(*it))
{
#ifdef DEBUG_ANIMATIONS
LOG(LINFO, ("Finish animation", (*it)->GetType(), ", rewind:", rewind,
", couldBeRewinded:", (*it)->CouldBeRewinded()));
changed = true;
#endif
it = lst.erase(it);
}
else
{
++it;
}
}
}
}
for (auto & anim : finishAnimations)
{
if (rewind && anim->CouldBeRewinded())
anim->Finish();
SaveAnimationResult(*anim);
}
#ifdef DEBUG_ANIMATIONS
if (changed)
Print();
#endif
if (frontList.empty())
StartNextAnimations();
}
void AnimationSystem::FinishAnimations(Animation::Type type, bool rewind, bool finishAll)
{
FinishAnimations([&type](std::shared_ptr<Animation> const & anim) { return anim->GetType() == type; }, rewind,
finishAll);
}
void AnimationSystem::FinishAnimations(Animation::Type type, std::string const & customType, bool rewind,
bool finishAll)
{
FinishAnimations([&type, &customType](std::shared_ptr<Animation> const & anim)
{ return anim->GetType() == type && anim->GetCustomType() == customType; }, rewind, finishAll);
}
void AnimationSystem::FinishObjectAnimations(Animation::Object object, bool rewind, bool finishAll)
{
FinishAnimations([&object](std::shared_ptr<Animation> const & anim) { return anim->HasObject(object); }, rewind,
finishAll);
}
void AnimationSystem::Advance(double elapsedSeconds)
{
if (!HasAnimations())
return;
TAnimationList finishedAnimations;
TAnimationList & frontList = *(m_animationChain.front());
for (auto it = frontList.begin(); it != frontList.end();)
{
auto & anim = *it;
anim->Advance(elapsedSeconds);
if (anim->IsFinished())
finishedAnimations.splice(finishedAnimations.end(), frontList, it++);
else
++it;
}
for (auto & anim : finishedAnimations)
{
anim->OnFinish();
SaveAnimationResult(*anim);
}
if (frontList.empty())
StartNextAnimations();
}
#ifdef DEBUG_ANIMATIONS
void AnimationSystem::Print()
{
LOG(LINFO, ("-----------------------Animation chain begin-----------------------"));
for (size_t i = 0, sz = m_animationChain.size(); i < sz; ++i)
{
auto & lst = *m_animationChain[i];
if (i > 0)
LOG(LINFO, ("- - - - - - - - - - - - Next parallel block - - - - - - - - - - - -"));
for (auto it = lst.begin(); it != lst.end(); ++it)
{
auto & anim = *it;
LOG(LINFO, ("Type:", anim->GetType()));
}
}
LOG(LINFO, ("========================Animation chain end========================"));
}
#endif
bool AnimationSystem::GetProperty(Animation::Object object, Animation::ObjectProperty property,
Animation::PropertyValue & value) const
{
if (!m_animationChain.empty())
{
PropertyBlender blender;
for (auto const & anim : *(m_animationChain.front()))
{
if (anim->HasProperty(object, property))
{
Animation::PropertyValue val;
if (anim->GetProperty(object, property, val))
blender.Blend(val);
}
}
if (!blender.IsEmpty())
{
value = blender.Finish();
return true;
}
}
auto it = m_propertyCache.find(std::make_pair(object, property));
if (it != m_propertyCache.end())
{
value = it->second;
m_propertyCache.erase(it);
return true;
}
return false;
}
bool AnimationSystem::GetTargetProperty(Animation::Object object, Animation::ObjectProperty property,
Animation::PropertyValue & value) const
{
if (!m_animationChain.empty())
{
PropertyBlender blender;
for (auto const & anim : *(m_animationChain.front()))
{
if (anim->HasTargetProperty(object, property))
{
Animation::PropertyValue val;
if (anim->GetTargetProperty(object, property, val))
blender.Blend(val);
}
}
if (!blender.IsEmpty())
{
value = blender.Finish();
return true;
}
}
auto it = m_propertyCache.find(std::make_pair(object, property));
if (it != m_propertyCache.end())
{
value = it->second;
return true;
}
return false;
}
void AnimationSystem::SaveAnimationResult(Animation const & animation)
{
for (auto const & object : animation.GetObjects())
{
for (auto const & property : animation.GetProperties(object))
{
Animation::PropertyValue value;
if (animation.GetProperty(object, property, value))
m_propertyCache[std::make_pair(object, property)] = value;
}
}
}
void AnimationSystem::StartNextAnimations()
{
if (m_animationChain.empty())
return;
m_animationChain.pop_front();
if (!m_animationChain.empty())
{
std::vector<std::weak_ptr<Animation>> startedAnimations;
startedAnimations.reserve(m_animationChain.front()->size());
for (auto & anim : *(m_animationChain.front()))
startedAnimations.push_back(anim);
for (auto & weak_anim : startedAnimations)
{
std::shared_ptr<Animation> anim = weak_anim.lock();
if (anim != nullptr)
{
anim->Init(m_lastScreen, m_propertyCache);
anim->OnStart();
}
}
}
}
} // namespace df

View file

@ -0,0 +1,98 @@
#pragma once
#include "drape_frontend/animation/animation.hpp"
#include "drape/drape_diagnostics.hpp"
#include "geometry/screenbase.hpp"
#include "base/macros.hpp"
#include <cstring>
#include <deque>
#include <functional>
#include <list>
#include <memory>
#include <string>
#include <utility>
namespace df
{
class AnimationSystem
{
public:
static AnimationSystem & Instance();
void UpdateLastScreen(ScreenBase const & currentScreen);
bool GetScreen(ScreenBase const & currentScreen, ScreenBase & screen);
void GetTargetScreen(ScreenBase const & currentScreen, ScreenBase & screen);
bool GetArrowPosition(m2::PointD & position);
bool GetArrowAngle(double & angle);
bool AnimationExists(Animation::Object object) const;
bool HasAnimations() const;
bool HasMapAnimations() const;
void CombineAnimation(drape_ptr<Animation> && animation);
void PushAnimation(drape_ptr<Animation> && animation);
void FinishAnimations(Animation::Type type, bool rewind, bool finishAll);
void FinishAnimations(Animation::Type type, std::string const & customType, bool rewind, bool finishAll);
void FinishObjectAnimations(Animation::Object object, bool rewind, bool finishAll);
template <typename T>
T const * FindAnimation(Animation::Type type, char const * customType = nullptr) const
{
for (auto & pList : m_animationChain)
{
auto & lst = *pList;
for (auto const & anim : lst)
{
if ((anim->GetType() == type) &&
(customType == nullptr || strcmp(anim->GetCustomType().c_str(), customType) == 0))
{
ASSERT(dynamic_cast<T const *>(anim.get()) != nullptr, ());
return static_cast<T const *>(anim.get());
}
}
}
return nullptr;
}
void Advance(double elapsedSeconds);
ScreenBase const & GetLastScreen() { return m_lastScreen; }
void SaveAnimationResult(Animation const & animation);
private:
AnimationSystem() = default;
using TGetPropertyFn = std::function<bool(Animation::Object object, Animation::ObjectProperty property,
Animation::PropertyValue & value)>;
bool GetScreen(ScreenBase const & currentScreen, TGetPropertyFn const & getPropertyFn, ScreenBase & screen);
bool GetProperty(Animation::Object object, Animation::ObjectProperty property,
Animation::PropertyValue & value) const;
bool GetTargetProperty(Animation::Object object, Animation::ObjectProperty property,
Animation::PropertyValue & value) const;
void StartNextAnimations();
void FinishAnimations(std::function<bool(std::shared_ptr<Animation> const &)> const & predicate, bool rewind,
bool finishAll);
#ifdef DEBUG_ANIMATIONS
void Print();
#endif
using TAnimationList = std::list<std::shared_ptr<Animation>>;
using TAnimationChain = std::deque<std::shared_ptr<TAnimationList>>;
using TPropertyCache = std::map<std::pair<Animation::Object, Animation::ObjectProperty>, Animation::PropertyValue>;
TAnimationChain m_animationChain;
mutable TPropertyCache m_propertyCache;
ScreenBase m_lastScreen;
DISALLOW_COPY_AND_MOVE(AnimationSystem);
};
} // namespace df

View file

@ -0,0 +1,15 @@
#include "drape_frontend/animation_utils.hpp"
#include "drape_frontend/animation_constants.hpp"
#include "drape_frontend/visual_params.hpp"
#include "indexer/scales.hpp"
namespace df
{
bool IsAnimationAllowed(double duration, ScreenBase const & screen)
{
return duration > 0.0 && duration <= kMaxAnimationTimeSec;
}
} // namespace df

View file

@ -0,0 +1,10 @@
#pragma once
#include "geometry/screenbase.hpp"
namespace df
{
bool IsAnimationAllowed(double duration, ScreenBase const & screen);
} // namespace df

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,198 @@
#pragma once
#include "drape_frontend/shape_view_params.hpp"
#include "drape_frontend/stylist.hpp"
#include "drape_frontend/tile_key.hpp"
#include "drape/drape_diagnostics.hpp"
#include "drape/pointers.hpp"
#include "indexer/drawing_rule_def.hpp"
#include "indexer/drawing_rules.hpp"
#include "indexer/road_shields_parser.hpp"
#include "geometry/point2d.hpp"
#include "geometry/spline.hpp"
#include <functional>
#include <vector>
class CaptionDefProto;
namespace dp
{
class TextureManager;
} // namespace dp
namespace df
{
struct TextViewParams;
class MapShape;
struct BuildingOutline;
using TInsertShapeFn = std::function<void(drape_ptr<MapShape> && shape)>;
class BaseApplyFeature
{
public:
BaseApplyFeature(TileKey const & tileKey, TInsertShapeFn const & insertShape, FeatureType & f,
CaptionDescription const & captions);
virtual ~BaseApplyFeature() = default;
protected:
void FillCommonParams(CommonOverlayViewParams & p) const;
double PriorityToDepth(int priority, drule::TypeT ruleType, double areaDepth) const;
TInsertShapeFn m_insertShape;
FeatureType & m_f;
CaptionDescription const & m_captions;
TileKey const m_tileKey;
m2::RectD const m_tileRect;
};
class ApplyPointFeature : public BaseApplyFeature
{
using TBase = BaseApplyFeature;
public:
ApplyPointFeature(TileKey const & tileKey, TInsertShapeFn const & insertShape, FeatureType & f,
CaptionDescription const & captions);
void ProcessPointRules(SymbolRuleProto const * symbolRule, CaptionRuleProto const * captionRule,
CaptionRuleProto const * houseNumberRule, m2::PointD const & centerPoint,
ref_ptr<dp::TextureManager> texMng);
protected:
void ExtractCaptionParams(CaptionDefProto const * primaryProto, CaptionDefProto const * secondaryProto,
TextViewParams & params) const;
float m_posZ = 0.0f;
private:
virtual bool HasArea() const { return false; }
};
class ApplyAreaFeature : public ApplyPointFeature
{
using TBase = ApplyPointFeature;
public:
ApplyAreaFeature(TileKey const & tileKey, TInsertShapeFn const & insertShape, FeatureType & f,
double currentScaleGtoP, bool isBuilding, float minPosZ, float posZ,
CaptionDescription const & captions);
void operator()(m2::PointD const & p1, m2::PointD const & p2, m2::PointD const & p3);
bool HasGeometry() const { return !m_triangles.empty(); }
void ProcessAreaRules(AreaRuleProto const * areaRule, AreaRuleProto const * hatchingRule);
struct Edge
{
Edge() = default;
Edge(int startIndex, int endIndex) : m_startIndex(startIndex), m_endIndex(endIndex) {}
bool operator==(Edge const & edge) const
{
return (m_startIndex == edge.m_startIndex && m_endIndex == edge.m_endIndex) ||
(m_startIndex == edge.m_endIndex && m_endIndex == edge.m_startIndex);
}
int m_startIndex = -1;
int m_endIndex = -1;
};
struct ExtendedEdge
{
ExtendedEdge() = default;
ExtendedEdge(Edge && edge, int internalVertexIndex, bool twoSide)
: m_edge(std::move(edge))
, m_internalVertexIndex(internalVertexIndex)
, m_twoSide(twoSide)
{}
Edge m_edge;
int m_internalVertexIndex = -1;
bool m_twoSide = false;
};
private:
bool HasArea() const override { return true; }
void ProcessRule(AreaRuleProto const & areaRule, double areaDepth, bool isHatching);
void ProcessBuildingPolygon(m2::PointD const & p1, m2::PointD const & p2, m2::PointD const & p3);
void CalculateBuildingOutline(bool calculateNormals, BuildingOutline & outline);
int GetIndex(m2::PointD const & pt);
void BuildEdges(int vertexIndex1, int vertexIndex2, int vertexIndex3, bool twoSide);
bool IsDuplicatedEdge(Edge const & edge);
m2::PointD CalculateNormal(m2::PointD const & p1, m2::PointD const & p2, m2::PointD const & p3) const;
std::vector<m2::PointD> m_triangles;
buffer_vector<m2::PointD, kBuildingOutlineSize> m_points;
buffer_vector<ExtendedEdge, kBuildingOutlineSize> m_edges;
float const m_minPosZ;
bool const m_isBuilding;
double const m_currentScaleGtoP;
};
class ApplyLineFeatureGeometry : public BaseApplyFeature
{
using TBase = BaseApplyFeature;
public:
ApplyLineFeatureGeometry(TileKey const & tileKey, TInsertShapeFn const & insertShape, FeatureType & f,
double currentScaleGtoP);
void operator()(m2::PointD const & point);
bool HasGeometry() const { return m_spline->IsValid(); }
void ProcessLineRules(Stylist::LineRulesT const & lineRules);
std::vector<m2::SharedSpline> const & GetClippedSplines() const { return m_clippedSplines; }
private:
void ProcessRule(LineRuleProto const & lineRule);
m2::SharedSpline m_spline;
std::vector<m2::SharedSpline> m_clippedSplines;
double const m_currentScaleGtoP;
double const m_minSegmentSqrLength;
m2::PointD m_lastAddedPoint;
bool const m_simplify;
#ifdef LINES_GENERATION_CALC_FILTERED_POINTS
int m_readCount = 0;
#endif
};
// Process pathtext and shield drules. Operates on metalines usually.
class ApplyLineFeatureAdditional : public BaseApplyFeature
{
using TBase = BaseApplyFeature;
public:
ApplyLineFeatureAdditional(TileKey const & tileKey, TInsertShapeFn const & insertShape, FeatureType & f,
double currentScaleGtoP, CaptionDescription const & captions,
std::vector<m2::SharedSpline> && clippedSplines);
void ProcessAdditionalLineRules(PathTextRuleProto const * pathtextRule, ShieldRuleProto const * shieldRule,
ref_ptr<dp::TextureManager> texMng, ftypes::RoadShieldsSetT const & roadShields,
GeneratedRoadShields & generatedRoadShields);
private:
void GetRoadShieldsViewParams(ref_ptr<dp::TextureManager> texMng, ftypes::RoadShield const & shield,
uint8_t shieldIndex, uint8_t shieldCount, TextViewParams & textParams,
ColoredSymbolViewParams & symbolParams, PoiSymbolViewParams & poiParams,
m2::PointD & shieldPixelSize);
bool CheckShieldsNearby(m2::PointD const & shieldPos, m2::PointD const & shieldPixelSize,
uint32_t minDistanceInPixels, std::vector<m2::RectD> & shields);
std::vector<m2::SharedSpline> m_clippedSplines;
double const m_currentScaleGtoP;
float m_captionDepth = 0.0f, m_shieldDepth = 0.0f;
CaptionDefProto const * m_captionRule = nullptr;
ShieldRuleProto const * m_shieldRule = nullptr;
};
extern dp::Color ToDrapeColor(uint32_t src);
} // namespace df

View file

@ -0,0 +1,184 @@
#include "drape_frontend/area_shape.hpp"
#include "drape_frontend/render_state_extension.hpp"
#include "shaders/programs.hpp"
#include "drape/attribute_provider.hpp"
#include "drape/batcher.hpp"
#include "drape/texture_manager.hpp"
#include "drape/utils/vertex_decl.hpp"
#include "base/buffer_vector.hpp"
#include <algorithm>
namespace df
{
AreaShape::AreaShape(std::vector<m2::PointD> triangleList, BuildingOutline && buildingOutline,
AreaViewParams const & params)
: m_vertexes(std::move(triangleList))
, m_buildingOutline(std::move(buildingOutline))
, m_params(params)
{}
void AreaShape::Draw(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::Batcher> batcher,
ref_ptr<dp::TextureManager> textures) const
{
dp::TextureManager::ColorRegion region;
textures->GetColorRegion(m_params.m_color, region);
m2::PointD const colorUv(region.GetTexRect().Center());
m2::PointD outlineUv(0.0, 0.0);
if (m_buildingOutline.m_generateOutline)
{
dp::TextureManager::ColorRegion outlineRegion;
textures->GetColorRegion(m_params.m_outlineColor, outlineRegion);
ASSERT_EQUAL(region.GetTexture(), outlineRegion.GetTexture(), ());
outlineUv = outlineRegion.GetTexRect().Center();
}
if (m_params.m_is3D)
DrawArea3D(context, batcher, colorUv, outlineUv, region.GetTexture());
else if (m_params.m_hatching)
DrawHatchingArea(context, batcher, colorUv, region.GetTexture(), textures->GetHatchingTexture());
else
DrawArea(context, batcher, colorUv, outlineUv, region.GetTexture());
}
void AreaShape::DrawArea(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::Batcher> batcher, m2::PointD const & colorUv,
m2::PointD const & outlineUv, ref_ptr<dp::Texture> texture) const
{
glsl::vec2 const uv = glsl::ToVec2(colorUv);
gpu::VBReservedSizeT<gpu::AreaVertex> vertexes;
vertexes.reserve(m_vertexes.size());
for (m2::PointD const & vertex : m_vertexes)
vertexes.emplace_back(ToShapeVertex3(vertex), uv);
auto const areaProgram = m_params.m_color.GetAlpha() == 255 ? gpu::Program::Area : gpu::Program::TransparentArea;
auto state = CreateRenderState(areaProgram, DepthLayer::GeometryLayer);
state.SetDepthTestEnabled(m_params.m_depthTestEnabled);
state.SetColorTexture(texture);
dp::AttributeProvider provider(1, static_cast<uint32_t>(vertexes.size()));
provider.InitStream(0, gpu::AreaVertex::GetBindingInfo(), make_ref(vertexes.data()));
batcher->InsertTriangleList(context, state, make_ref(&provider));
// Generate outline.
if (m_buildingOutline.m_generateOutline && !m_buildingOutline.m_indices.empty())
{
glsl::vec2 const ouv = glsl::ToVec2(outlineUv);
gpu::VBReservedSizeT<gpu::AreaVertex> vertices;
vertices.reserve(m_buildingOutline.m_vertices.size());
for (m2::PointD const & vertex : m_buildingOutline.m_vertices)
vertices.emplace_back(ToShapeVertex3(vertex), ouv);
auto outlineState = CreateRenderState(gpu::Program::AreaOutline, DepthLayer::GeometryLayer);
outlineState.SetDepthTestEnabled(m_params.m_depthTestEnabled);
outlineState.SetColorTexture(texture);
outlineState.SetDrawAsLine(true);
dp::AttributeProvider outlineProvider(1, static_cast<uint32_t>(vertices.size()));
outlineProvider.InitStream(0, gpu::AreaVertex::GetBindingInfo(), make_ref(vertices.data()));
batcher->InsertLineRaw(context, outlineState, make_ref(&outlineProvider), m_buildingOutline.m_indices);
}
}
void AreaShape::DrawHatchingArea(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::Batcher> batcher,
m2::PointD const & colorUv, ref_ptr<dp::Texture> texture,
ref_ptr<dp::Texture> hatchingTexture) const
{
glsl::vec2 const uv = glsl::ToVec2(colorUv);
m2::RectD bbox;
for (auto const & v : m_vertexes)
bbox.Add(v);
double const maxU = m_params.m_baseGtoPScale / hatchingTexture->GetWidth();
double const maxV = m_params.m_baseGtoPScale / hatchingTexture->GetHeight();
gpu::VBReservedSizeT<gpu::HatchingAreaVertex> vertexes;
vertexes.reserve(m_vertexes.size());
for (m2::PointD const & vertex : m_vertexes)
{
vertexes.emplace_back(ToShapeVertex3(vertex), uv,
glsl::vec2(static_cast<float>(maxU * (vertex.x - bbox.minX())),
static_cast<float>(maxV * (vertex.y - bbox.minY()))));
}
auto state = CreateRenderState(gpu::Program::HatchingArea, DepthLayer::GeometryLayer);
state.SetDepthTestEnabled(m_params.m_depthTestEnabled);
state.SetColorTexture(texture);
state.SetMaskTexture(hatchingTexture);
state.SetTextureFilter(dp::TextureFilter::Linear);
dp::AttributeProvider provider(1, static_cast<uint32_t>(vertexes.size()));
provider.InitStream(0, gpu::HatchingAreaVertex::GetBindingInfo(), make_ref(vertexes.data()));
batcher->InsertTriangleList(context, state, make_ref(&provider));
}
void AreaShape::DrawArea3D(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::Batcher> batcher,
m2::PointD const & colorUv, m2::PointD const & outlineUv, ref_ptr<dp::Texture> texture) const
{
CHECK(!m_buildingOutline.m_indices.empty(), ());
CHECK_EQUAL(m_buildingOutline.m_normals.size() * 2, m_buildingOutline.m_indices.size(), ());
glsl::vec2 const uv = glsl::ToVec2(colorUv);
gpu::VBReservedSizeT<gpu::Area3dVertex> vertexes;
vertexes.reserve(m_vertexes.size() + m_buildingOutline.m_normals.size() * 6);
for (size_t i = 0; i < m_buildingOutline.m_normals.size(); i++)
{
int const startIndex = m_buildingOutline.m_indices[i * 2];
int const endIndex = m_buildingOutline.m_indices[i * 2 + 1];
glsl::vec2 const startPt = ToShapeVertex2(m_buildingOutline.m_vertices[startIndex]);
glsl::vec2 const endPt = ToShapeVertex2(m_buildingOutline.m_vertices[endIndex]);
glsl::vec3 normal(glsl::ToVec2(m_buildingOutline.m_normals[i]), 0.0f);
vertexes.emplace_back(glsl::vec3(startPt, -m_params.m_minPosZ), normal, uv);
vertexes.emplace_back(glsl::vec3(endPt, -m_params.m_minPosZ), normal, uv);
vertexes.emplace_back(glsl::vec3(startPt, -m_params.m_posZ), normal, uv);
vertexes.emplace_back(glsl::vec3(startPt, -m_params.m_posZ), normal, uv);
vertexes.emplace_back(glsl::vec3(endPt, -m_params.m_minPosZ), normal, uv);
vertexes.emplace_back(glsl::vec3(endPt, -m_params.m_posZ), normal, uv);
}
glsl::vec3 const normal(0.0f, 0.0f, -1.0f);
for (m2::PointD const & vertex : m_vertexes)
vertexes.emplace_back(glsl::vec3(ToShapeVertex2(vertex), -m_params.m_posZ), normal, uv);
auto state = CreateRenderState(gpu::Program::Area3d, DepthLayer::Geometry3dLayer);
state.SetDepthTestEnabled(m_params.m_depthTestEnabled);
state.SetColorTexture(texture);
state.SetBlending(dp::Blending(false /* isEnabled */));
dp::AttributeProvider provider(1, static_cast<uint32_t>(vertexes.size()));
provider.InitStream(0, gpu::Area3dVertex::GetBindingInfo(), make_ref(vertexes.data()));
batcher->InsertTriangleList(context, state, make_ref(&provider));
// Generate outline.
if (m_buildingOutline.m_generateOutline)
{
glsl::vec2 const ouv = glsl::ToVec2(outlineUv);
auto outlineState = CreateRenderState(gpu::Program::Area3dOutline, DepthLayer::Geometry3dLayer);
outlineState.SetDepthTestEnabled(m_params.m_depthTestEnabled);
outlineState.SetColorTexture(texture);
outlineState.SetBlending(dp::Blending(false /* isEnabled */));
outlineState.SetDrawAsLine(true);
gpu::VBReservedSizeT<gpu::AreaVertex> olVertexes;
olVertexes.reserve(m_buildingOutline.m_vertices.size());
for (m2::PointD const & vertex : m_buildingOutline.m_vertices)
olVertexes.emplace_back(glsl::vec3(ToShapeVertex2(vertex), -m_params.m_posZ), ouv);
dp::AttributeProvider outlineProvider(1, static_cast<uint32_t>(olVertexes.size()));
outlineProvider.InitStream(0, gpu::AreaVertex::GetBindingInfo(), make_ref(olVertexes.data()));
batcher->InsertLineRaw(context, outlineState, make_ref(&outlineProvider), m_buildingOutline.m_indices);
}
}
} // namespace df

View file

@ -0,0 +1,50 @@
#pragma once
#include "drape_frontend/map_shape.hpp"
#include "drape_frontend/shape_view_params.hpp"
#include "drape/glsl_types.hpp"
#include "drape/pointers.hpp"
#include "geometry/point2d.hpp"
#include <vector>
namespace df
{
struct BuildingOutline
{
buffer_vector<m2::PointD, kBuildingOutlineSize> m_vertices;
std::vector<int> m_indices;
std::vector<m2::PointD> m_normals;
bool m_generateOutline = false;
};
class AreaShape : public MapShape
{
public:
AreaShape(std::vector<m2::PointD> triangleList, BuildingOutline && buildingOutline, AreaViewParams const & params);
void Draw(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::Batcher> batcher,
ref_ptr<dp::TextureManager> textures) const override;
private:
glsl::vec2 ToShapeVertex2(m2::PointD const & vertex) const
{
return glsl::ToVec2(ConvertToLocal(vertex, m_params.m_tileCenter, kShapeCoordScalar));
}
glsl::vec3 ToShapeVertex3(m2::PointD const & vertex) const { return {ToShapeVertex2(vertex), m_params.m_depth}; }
void DrawArea(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::Batcher> batcher, m2::PointD const & colorUv,
m2::PointD const & outlineUv, ref_ptr<dp::Texture> texture) const;
void DrawArea3D(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::Batcher> batcher, m2::PointD const & colorUv,
m2::PointD const & outlineUv, ref_ptr<dp::Texture> texture) const;
void DrawHatchingArea(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::Batcher> batcher, m2::PointD const & colorUv,
ref_ptr<dp::Texture> texture, ref_ptr<dp::Texture> hatchingTexture) const;
std::vector<m2::PointD> m_vertexes;
BuildingOutline m_buildingOutline;
AreaViewParams m_params;
};
} // namespace df

View file

@ -0,0 +1,502 @@
#include "drape_frontend/arrow3d.hpp"
#include "drape_frontend/color_constants.hpp"
#include "drape_frontend/visual_params.hpp"
#include "shaders/program_manager.hpp"
#include "drape/glsl_func.hpp"
#include "drape/pointers.hpp"
#include "drape/static_texture.hpp"
#include "drape/texture_manager.hpp"
#include "indexer/map_style_reader.hpp"
#include "platform/platform.hpp"
#include "coding/reader.hpp"
#include "geometry/screenbase.hpp"
#include <cstring>
#include <string_view>
#define FAST_OBJ_IMPLEMENTATION
#include "3party/fast_obj/fast_obj.h"
namespace df
{
namespace arrow3d
{
double constexpr kArrowSize = 12.0;
double constexpr kArrow3dScaleMin = 1.0;
double constexpr kArrow3dScaleMax = 2.2;
int constexpr kArrow3dMinZoom = 16;
} // namespace arrow3d
namespace
{
float constexpr kOutlineScale = 1.2f;
int constexpr kComponentsInVertex = 3;
int constexpr kComponentsInNormal = 3;
int constexpr kComponentsInTexCoord = 2;
df::ColorConstant const kArrow3DShadowColor = "Arrow3DShadow";
df::ColorConstant const kArrow3DObsoleteColor = "Arrow3DObsolete";
df::ColorConstant const kArrow3DColor = "Arrow3D";
df::ColorConstant const kArrow3DOutlineColor = "Arrow3DOutline";
std::string const kDefaultArrowMesh = "arrow.obj";
std::string const kDefaultArrowShadowMesh = "arrow_shadow.obj";
std::string_view constexpr kMainFileId = "main_obj_file_id";
using TLoadingCompletion =
std::function<void(std::vector<float> positions, std::vector<float> normals, std::vector<float> texCoords)>;
using TLoadingFailure = std::function<void(std::string const &)>;
namespace fast_obj_adapter
{
void * FileOpen(char const * path, void * userData)
{
// Load only the main OBJ file, skip all the files that can be referred
// inside the OBJ model.
if (kMainFileId != path)
return nullptr;
return userData;
}
void FileClose(void * file, void * userData)
{
// Do nothing.
}
size_t FileRead(void * file, void * dst, size_t bytes, void * userData)
{
auto reader = static_cast<ReaderSource<ReaderPtr<Reader>> *>(userData);
CHECK(reader != nullptr, ());
auto const sz = reader->Size();
if (sz == 0)
return 0;
if (bytes > sz)
bytes = static_cast<size_t>(sz);
auto const p = reader->Pos();
reader->Read(dst, bytes);
CHECK_LESS_OR_EQUAL(p, reader->Pos(), ());
return static_cast<size_t>(reader->Pos() - p);
}
unsigned long FileSize(void * file, void * userData)
{
auto reader = static_cast<ReaderSource<ReaderPtr<Reader>> *>(userData);
CHECK(reader != nullptr, ());
CHECK_LESS(reader->Size(), static_cast<uint64_t>(std::numeric_limits<size_t>::max()), ());
return static_cast<size_t>(reader->Size());
}
} // namespace fast_obj_adapter
class FastObjMeshGuard
{
public:
explicit FastObjMeshGuard(fastObjMesh * mesh) : m_mesh(mesh) {}
~FastObjMeshGuard()
{
if (m_mesh)
fast_obj_destroy(m_mesh);
}
private:
fastObjMesh * const m_mesh;
};
bool LoadMesh(std::string const & pathToMesh, bool isDefaultResource, TLoadingCompletion const & completionHandler,
TLoadingFailure const & failureHandler)
{
CHECK(completionHandler != nullptr, ());
CHECK(failureHandler != nullptr, ());
fastObjMesh * meshData = nullptr;
try
{
ReaderPtr<Reader> reader =
isDefaultResource ? GetStyleReader().GetDefaultResourceReader(pathToMesh) : GetPlatform().GetReader(pathToMesh);
ReaderSource source(reader);
// Read OBJ file.
fastObjCallbacks callbacks;
callbacks.file_open = fast_obj_adapter::FileOpen;
callbacks.file_close = fast_obj_adapter::FileClose;
callbacks.file_read = fast_obj_adapter::FileRead;
callbacks.file_size = fast_obj_adapter::FileSize;
meshData = fast_obj_read_with_callbacks(kMainFileId.data(), &callbacks, &source);
CHECK(meshData != nullptr, ());
FastObjMeshGuard guard(meshData);
// Fill buffers.
std::vector<float> positions;
if (meshData->position_count > 1)
positions.resize(meshData->index_count * kComponentsInVertex);
std::vector<float> normals;
if (meshData->normal_count > 1)
normals.resize(meshData->index_count * kComponentsInNormal);
std::vector<float> texCoords;
if (meshData->texcoord_count > 1)
texCoords.resize(meshData->index_count * kComponentsInTexCoord);
for (uint32_t i = 0; i < meshData->index_count; ++i)
{
if (meshData->position_count > 1)
{
memcpy(&positions[i * kComponentsInVertex], &meshData->positions[meshData->indices[i].p * kComponentsInVertex],
sizeof(float) * kComponentsInVertex);
}
if (meshData->normal_count > 1)
{
memcpy(&normals[i * kComponentsInNormal], &meshData->normals[meshData->indices[i].n * kComponentsInNormal],
sizeof(float) * kComponentsInNormal);
}
if (meshData->texcoord_count > 1)
{
memcpy(&texCoords[i * kComponentsInTexCoord],
&meshData->texcoords[meshData->indices[i].t * kComponentsInTexCoord],
sizeof(float) * kComponentsInTexCoord);
}
}
completionHandler(std::move(positions), std::move(normals), std::move(texCoords));
}
catch (RootException & e)
{
failureHandler(e.what());
return false;
}
return true;
}
} // namespace
// static
Arrow3d::PreloadedData Arrow3d::PreloadMesh(std::optional<Arrow3dCustomDecl> const & customDecl,
ref_ptr<dp::TextureManager> texMng)
{
Arrow3d::PreloadedData data;
bool const useDefaultResource = !customDecl || customDecl->m_loadFromDefaultResourceFolder;
// Load arrow mesh.
auto const & meshPath = customDecl ? customDecl->m_arrowMeshPath : kDefaultArrowMesh;
data.m_meshData = PreloadedMeshData{};
if (!LoadMesh(meshPath, useDefaultResource,
[&](std::vector<float> positions, std::vector<float> normals, std::vector<float> texCoords)
{
if (!positions.empty())
{
if (normals.empty())
normals = dp::MeshObject::GenerateNormalsForTriangles(positions, kComponentsInNormal);
data.m_meshData->m_positions = std::move(positions);
data.m_meshData->m_normals = std::move(normals);
// Texture coordinates.
ref_ptr<dp::StaticTexture> arrowTexture = texMng->GetArrowTexture();
CHECK(arrowTexture != nullptr, ("Arrow texture must be initialized before the mesh"));
// NOTE: texture must be loaded before the mesh.
if (arrowTexture->IsLoadingCorrect() && !texCoords.empty())
{
data.m_arrowMeshTexturingEnabled = true;
data.m_meshData->m_texCoords = std::move(texCoords);
}
else
{
data.m_arrowMeshTexturingEnabled = false;
}
}
else
{
data.m_meshData.reset();
}
}, [&](std::string const & reason)
{
data.m_meshData.reset();
LOG(LERROR, ("Arrow3D mesh was not loaded:", reason));
}))
{
return {};
}
// Load shadow arrow mesh.
auto const & shadowMeshPath = customDecl ? customDecl->m_shadowMeshPath : kDefaultArrowShadowMesh;
if (!shadowMeshPath.empty())
{
data.m_shadowMeshData = PreloadedMeshData{};
LoadMesh(shadowMeshPath, useDefaultResource,
[&](std::vector<float> positions, std::vector<float> /* normals */, std::vector<float> texCoords)
{
// NOTE: Shadow mesh must contain texture coordinates. They're used to create soft shadow.
if (!positions.empty() && !texCoords.empty())
{
data.m_shadowMeshData->m_positions = std::move(positions);
data.m_shadowMeshData->m_texCoords = std::move(texCoords);
}
else
{
data.m_shadowMeshData.reset();
}
}, [&](std::string const & reason)
{
data.m_shadowMeshData.reset();
LOG(LWARNING, ("Arrow3D shadow mesh was not loaded:", reason));
});
}
if (customDecl.has_value())
{
data.m_texCoordFlipping =
glsl::vec2{customDecl->m_flipTexCoordU ? 1.0f : 0.0f, customDecl->m_flipTexCoordV ? 1.0f : 0.0f};
data.m_meshOffset = glsl::vec3{customDecl->m_offset.x, customDecl->m_offset.y, customDecl->m_offset.z};
data.m_meshEulerAngles =
glsl::vec3{customDecl->m_eulerAngles.x, customDecl->m_eulerAngles.y, customDecl->m_eulerAngles.z};
data.m_meshScale = glsl::vec3{customDecl->m_scale.x, customDecl->m_scale.y, customDecl->m_scale.z};
data.m_enableShadow = customDecl->m_enableShadow;
data.m_enableOutline = customDecl->m_enableOutline;
}
return data;
}
Arrow3d::Arrow3d(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::TextureManager> texMng,
PreloadedData && preloadedData)
: m_arrowMesh(context, dp::MeshObject::DrawPrimitive::Triangles, "Arrow3d")
, m_arrowMeshTexturingEnabled(preloadedData.m_arrowMeshTexturingEnabled)
, m_texCoordFlipping(std::move(preloadedData.m_texCoordFlipping))
, m_shadowMesh(
preloadedData.m_shadowMeshData.has_value()
? make_unique_dp<dp::MeshObject>(context, dp::MeshObject::DrawPrimitive::Triangles, "Arrow3dShadow")
: nullptr)
, m_state(CreateRenderState(gpu::Program::Arrow3d, DepthLayer::OverlayLayer))
, m_meshOffset(std::move(preloadedData.m_meshOffset))
, m_meshEulerAngles(std::move(preloadedData.m_meshEulerAngles))
, m_meshScale(std::move(preloadedData.m_meshScale))
, m_enableShadow(preloadedData.m_enableShadow)
, m_enableOutline(preloadedData.m_enableOutline)
{
m_state.SetDepthTestEnabled(true);
m_isValid = preloadedData.m_meshData.has_value();
// Init arrow mesh.
auto constexpr kVerticesBufferInd = 0;
auto constexpr kNormalsBufferInd = 1;
auto constexpr kTexCoordBufferInd = 2;
if (m_isValid)
{
// Positions.
CHECK(!preloadedData.m_meshData->m_positions.empty(), ());
m_arrowMesh.SetBuffer(kVerticesBufferInd, std::move(preloadedData.m_meshData->m_positions),
sizeof(float) * kComponentsInVertex);
m_arrowMesh.SetAttribute("a_pos", kVerticesBufferInd, 0 /* offset */, kComponentsInVertex);
// Normals.
CHECK(!preloadedData.m_meshData->m_normals.empty(), ());
m_arrowMesh.SetBuffer(kNormalsBufferInd, std::move(preloadedData.m_meshData->m_normals),
sizeof(float) * kComponentsInNormal);
m_arrowMesh.SetAttribute("a_normal", kNormalsBufferInd, 0 /* offset */, kComponentsInNormal);
// Texture coordinates.
if (m_arrowMeshTexturingEnabled)
{
CHECK(!preloadedData.m_meshData->m_texCoords.empty(), ());
m_state.SetColorTexture(texMng->GetArrowTexture());
m_arrowMesh.SetBuffer(kTexCoordBufferInd, std::move(preloadedData.m_meshData->m_texCoords),
sizeof(float) * kComponentsInTexCoord);
m_arrowMesh.SetAttribute("a_texCoords", kTexCoordBufferInd, 0 /* offset */, kComponentsInTexCoord);
}
}
// Load shadow arrow mesh.
if (m_isValid && preloadedData.m_shadowMeshData.has_value())
{
CHECK(m_shadowMesh != nullptr, ());
auto constexpr kTexCoordShadowBufferInd = 1;
// Positions.
CHECK(!preloadedData.m_shadowMeshData->m_positions.empty(), ());
m_shadowMesh->SetBuffer(kVerticesBufferInd, std::move(preloadedData.m_shadowMeshData->m_positions),
sizeof(float) * kComponentsInVertex);
m_shadowMesh->SetAttribute("a_pos", kVerticesBufferInd, 0 /* offset */, kComponentsInVertex);
// Texture coordinates.
CHECK(!preloadedData.m_shadowMeshData->m_texCoords.empty(), ());
m_shadowMesh->SetBuffer(kTexCoordShadowBufferInd, std::move(preloadedData.m_shadowMeshData->m_texCoords),
sizeof(float) * kComponentsInTexCoord);
m_shadowMesh->SetAttribute("a_texCoords", kTexCoordShadowBufferInd, 0 /* offset */, kComponentsInTexCoord);
}
}
bool Arrow3d::IsValid() const
{
return m_isValid;
}
// static
double Arrow3d::GetMaxBottomSize()
{
double constexpr kBottomSize = 1.0;
return kBottomSize * arrow3d::kArrowSize * arrow3d::kArrow3dScaleMax * kOutlineScale;
}
void Arrow3d::SetPosition(m2::PointD const & position)
{
m_position = position;
}
void Arrow3d::SetAzimuth(double azimuth)
{
m_azimuth = azimuth;
}
void Arrow3d::SetPositionObsolete(bool obsolete)
{
m_obsoletePosition = obsolete;
}
void Arrow3d::SetMeshOffset(glsl::vec3 const & offset)
{
m_meshOffset = offset;
}
void Arrow3d::SetMeshRotation(glsl::vec3 const & eulerAngles)
{
m_meshEulerAngles = eulerAngles;
}
void Arrow3d::SetMeshScale(glsl::vec3 const & scale)
{
m_meshScale = scale;
}
void Arrow3d::SetShadowEnabled(bool enabled)
{
m_enableShadow = enabled;
}
void Arrow3d::SetOutlineEnabled(bool enabled)
{
m_enableOutline = enabled;
}
void Arrow3d::Render(ref_ptr<dp::GraphicsContext> context, ref_ptr<gpu::ProgramManager> mng, ScreenBase const & screen,
bool routingMode)
{
// Render shadow.
if (m_shadowMesh && m_enableShadow && screen.isPerspective())
{
RenderArrow(context, mng, *m_shadowMesh, screen, gpu::Program::Arrow3dShadow,
df::GetColorConstant(df::kArrow3DShadowColor), 0.05f /* dz */,
routingMode ? kOutlineScale : 1.0f /* scaleFactor */);
}
// Render outline.
if (m_shadowMesh && m_enableOutline && routingMode)
{
dp::Color const outlineColor = df::GetColorConstant(df::kArrow3DOutlineColor);
RenderArrow(context, mng, *m_shadowMesh, screen, gpu::Program::Arrow3dOutline, outlineColor, 0.0f /* dz */,
kOutlineScale /* scaleFactor */);
}
// Render arrow.
if (m_arrowMeshTexturingEnabled)
{
// Use only alpha channel from arrow color for textured meshes.
auto const color = dp::Color(255, 255, 255,
m_obsoletePosition ? df::GetColorConstant(df::kArrow3DObsoleteColor).GetAlpha()
: df::GetColorConstant(df::kArrow3DColor).GetAlpha());
RenderArrow(context, mng, m_arrowMesh, screen, gpu::Program::Arrow3dTextured, color, 0.0f /* dz */,
1.0f /* scaleFactor */);
}
else
{
dp::Color const color = df::GetColorConstant(m_obsoletePosition ? df::kArrow3DObsoleteColor : df::kArrow3DColor);
RenderArrow(context, mng, m_arrowMesh, screen, gpu::Program::Arrow3d, color, 0.0f /* dz */, 1.0f /* scaleFactor */);
}
}
void Arrow3d::RenderArrow(ref_ptr<dp::GraphicsContext> context, ref_ptr<gpu::ProgramManager> mng, dp::MeshObject & mesh,
ScreenBase const & screen, gpu::Program program, dp::Color const & color, float dz,
float scaleFactor)
{
gpu::Arrow3dProgramParams params;
auto [transform, normalTransform] = CalculateTransform(screen, dz, scaleFactor, context->GetApiVersion());
params.m_transform = std::move(transform);
params.m_normalTransform = std::move(normalTransform);
params.m_color = glsl::ToVec4(color);
params.m_texCoordFlipping = m_texCoordFlipping;
auto gpuProgram = mng->GetProgram(program);
mesh.Render(context, gpuProgram, m_state, mng->GetParamsSetter(), params);
}
std::pair<glsl::mat4, glsl::mat4> Arrow3d::CalculateTransform(ScreenBase const & screen, float dz, float scaleFactor,
dp::ApiVersion apiVersion) const
{
double arrowScale = VisualParams::Instance().GetVisualScale() * arrow3d::kArrowSize * scaleFactor;
if (screen.isPerspective())
{
double const t = GetNormalizedZoomLevel(screen.GetScale(), arrow3d::kArrow3dMinZoom);
arrowScale *= (arrow3d::kArrow3dScaleMin * (1.0 - t) + arrow3d::kArrow3dScaleMax * t);
}
glm::quat const qx = glm::angleAxis(m_meshEulerAngles.x, glm::vec3{1.0f, 0.0f, 0.0f});
glm::quat const qy = glm::angleAxis(m_meshEulerAngles.y, glm::vec3{0.0f, 1.0f, 0.0f});
glm::quat qz = glm::angleAxis(static_cast<float>(m_azimuth + screen.GetAngle() + m_meshEulerAngles.z),
glm::vec3{0.0f, 0.0f, -1.0f});
auto const rotationMatrix = glm::mat4_cast(qz * qy * qx);
qz = glm::angleAxis(static_cast<float>(m_meshEulerAngles.z), glm::vec3{0.0f, 0.0f, -1.0f});
auto const normalMatrix = glm::mat4_cast(qz * qy * qx);
auto const scaleMatrix = glm::scale(
glm::mat4(1.0f), glm::vec3{arrowScale, arrowScale, screen.isPerspective() ? arrowScale : 1.0} * m_meshScale);
auto const translationMatrix = glm::translate(glm::mat4(1.0f), m_meshOffset);
auto postProjectionScale = glm::vec3{2.0f / screen.PixelRect().SizeX(), 2.0f / screen.PixelRect().SizeY(), 1.0f};
postProjectionScale.z =
screen.isPerspective() ? std::min(postProjectionScale.x, postProjectionScale.y) * screen.GetScale3d() : 0.1f;
auto const postProjectionScaleMatrix = glm::scale(glm::mat4(1.0f), postProjectionScale);
m2::PointD const pos = screen.GtoP(m_position);
auto const dX = static_cast<float>(2.0 * pos.x / screen.PixelRect().SizeX() - 1.0);
auto const dY = static_cast<float>(2.0 * pos.y / screen.PixelRect().SizeY() - 1.0);
auto const postProjectionTranslationMatrix = glm::translate(glm::mat4(1.0f), glm::vec3{dX, -dY, dz});
auto modelTransform =
postProjectionTranslationMatrix * postProjectionScaleMatrix * scaleMatrix * translationMatrix * rotationMatrix;
if (screen.isPerspective())
{
glm::mat4 pTo3dView = glm::make_mat4x4(screen.Pto3dMatrix().m_data);
auto postProjectionPerspective = pTo3dView * modelTransform;
return std::make_pair(postProjectionPerspective, normalMatrix);
}
if (apiVersion == dp::ApiVersion::Metal)
{
modelTransform[3][2] = modelTransform[3][2] + 0.5f;
modelTransform[2][2] = modelTransform[2][2] * 0.5f;
}
return std::make_pair(modelTransform, normalMatrix);
}
} // namespace df

View file

@ -0,0 +1,113 @@
#pragma once
#include "drape_frontend/drape_engine_params.hpp"
#include "drape_frontend/render_state_extension.hpp"
#include "drape/color.hpp"
#include "drape/glsl_types.hpp"
#include "drape/mesh_object.hpp"
#include "geometry/rect2d.hpp"
#include <optional>
#include <string>
#include <vector>
namespace dp
{
class GpuProgram;
class TextureManager;
} // namespace dp
namespace gpu
{
class ProgramManager;
} // namespace gpu
class ScreenBase;
namespace df
{
class Arrow3d
{
using Base = dp::MeshObject;
public:
struct PreloadedMeshData
{
std::vector<float> m_positions;
std::vector<float> m_normals;
std::vector<float> m_texCoords;
};
struct PreloadedData
{
std::optional<PreloadedMeshData> m_meshData;
std::optional<PreloadedMeshData> m_shadowMeshData;
bool m_arrowMeshTexturingEnabled = false;
glsl::vec2 m_texCoordFlipping{0.0f, 1.0f};
glsl::vec3 m_meshOffset{0.0f, 0.0f, 0.0f};
glsl::vec3 m_meshEulerAngles{0.0f, 0.0f, 0.0f};
glsl::vec3 m_meshScale{1.0f, 1.0f, 1.0f};
bool m_enableShadow = true;
bool m_enableOutline = true;
};
static PreloadedData PreloadMesh(std::optional<Arrow3dCustomDecl> const & customDecl,
ref_ptr<dp::TextureManager> texMng);
Arrow3d(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::TextureManager> texMng, PreloadedData && preloadedData);
bool IsValid() const;
static double GetMaxBottomSize();
void SetPosition(m2::PointD const & position);
void SetAzimuth(double azimuth);
void SetPositionObsolete(bool obsolete);
// Leyout is axes (in the plane of map): x - right, y - up,
// -z - perpendicular to the map's plane directed towards the observer.
// Offset is in local coordinates (model's coordinates).
void SetMeshOffset(glsl::vec3 const & offset);
void SetMeshRotation(glsl::vec3 const & eulerAngles);
void SetMeshScale(glsl::vec3 const & scale);
void SetShadowEnabled(bool enabled);
void SetOutlineEnabled(bool enabled);
void Render(ref_ptr<dp::GraphicsContext> context, ref_ptr<gpu::ProgramManager> mng, ScreenBase const & screen,
bool routingMode);
private:
// Returns transform matrix and normal transform matrix.
std::pair<glsl::mat4, glsl::mat4> CalculateTransform(ScreenBase const & screen, float dz, float scaleFactor,
dp::ApiVersion apiVersion) const;
void RenderArrow(ref_ptr<dp::GraphicsContext> context, ref_ptr<gpu::ProgramManager> mng, dp::MeshObject & mesh,
ScreenBase const & screen, gpu::Program program, dp::Color const & color, float dz,
float scaleFactor);
dp::MeshObject m_arrowMesh;
bool const m_arrowMeshTexturingEnabled;
glsl::vec2 const m_texCoordFlipping{0.0f, 1.0f}; // Y is flipped by default.
drape_ptr<dp::MeshObject> m_shadowMesh;
bool m_isValid = false;
m2::PointD m_position;
double m_azimuth = 0.0;
bool m_obsoletePosition = false;
dp::RenderState m_state;
glsl::vec3 m_meshOffset{0.0f, 0.0f, 0.0f};
glsl::vec3 m_meshEulerAngles{0.0f, 0.0f, 0.0f};
glsl::vec3 m_meshScale{1.0f, 1.0f, 1.0f};
bool m_enableShadow = true;
bool m_enableOutline = true;
};
} // namespace df

View file

@ -0,0 +1,809 @@
#include "drape_frontend/gui/drape_gui.hpp"
#include "drape_frontend/backend_renderer.hpp"
#include "drape_frontend/batchers_pool.hpp"
#include "drape_frontend/circles_pack_shape.hpp"
#include "drape_frontend/drape_api_builder.hpp"
#include "drape_frontend/drape_measurer.hpp"
#include "drape_frontend/map_shape.hpp"
#include "drape_frontend/message_subclasses.hpp"
#include "drape_frontend/metaline_manager.hpp"
#include "drape_frontend/read_manager.hpp"
#include "drape_frontend/route_builder.hpp"
#include "drape_frontend/selection_shape_generator.hpp"
#include "drape_frontend/user_mark_shapes.hpp"
#include "drape_frontend/visual_params.hpp"
#include "drape/support_manager.hpp"
#include "drape/texture_manager.hpp"
#include "indexer/scales.hpp"
#include "platform/platform.hpp"
#include "base/file_name_utils.hpp"
#include "base/logging.hpp"
#include <algorithm>
#include <utility>
using namespace std::placeholders;
namespace df
{
BackendRenderer::BackendRenderer(Params && params)
: BaseRenderer(ThreadsCommutator::ResourceUploadThread, params)
, m_model(params.m_model)
, m_readManager(make_unique_dp<ReadManager>(params.m_commutator, m_model, params.m_allow3dBuildings,
params.m_trafficEnabled, params.m_isolinesEnabled))
, m_transitBuilder(
make_unique_dp<TransitSchemeBuilder>(std::bind(&BackendRenderer::FlushTransitRenderData, this, _1)))
, m_trafficGenerator(make_unique_dp<TrafficGenerator>(std::bind(&BackendRenderer::FlushTrafficRenderData, this, _1)))
, m_userMarkGenerator(
make_unique_dp<UserMarkGenerator>(std::bind(&BackendRenderer::FlushUserMarksRenderData, this, _1)))
, m_requestedTiles(params.m_requestedTiles)
, m_updateCurrentCountryFn(params.m_updateCurrentCountryFn)
, m_metalineManager(make_unique_dp<MetalineManager>(params.m_commutator, m_model))
, m_arrow3dCustomDecl(std::move(params.m_arrow3dCustomDecl))
{
#ifdef DEBUG
m_isTeardowned = false;
#endif
TrafficGenerator::SetSimplifiedColorSchemeEnabled(params.m_simplifiedTrafficColors);
ASSERT(m_updateCurrentCountryFn != nullptr, ());
m_routeBuilder = make_unique_dp<RouteBuilder>(
[this](drape_ptr<SubrouteData> && subrouteData)
{
m_commutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<FlushSubrouteMessage>(std::move(subrouteData)), MessagePriority::Normal);
},
[this](drape_ptr<SubrouteArrowsData> && subrouteArrowsData)
{
m_commutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<FlushSubrouteArrowsMessage>(std::move(subrouteArrowsData)),
MessagePriority::Normal);
}, [this](drape_ptr<SubrouteMarkersData> && subrouteMarkersData)
{
m_commutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<FlushSubrouteMarkersMessage>(std::move(subrouteMarkersData)),
MessagePriority::Normal);
});
StartThread();
}
BackendRenderer::~BackendRenderer()
{
ASSERT(m_isTeardowned, ());
}
void BackendRenderer::Teardown()
{
StopThread();
#ifdef DEBUG
m_isTeardowned = true;
#endif
}
std::unique_ptr<threads::IRoutine> BackendRenderer::CreateRoutine()
{
return std::make_unique<Routine>(*this);
}
void BackendRenderer::RecacheGui(gui::TWidgetsInitInfo const & initInfo, bool needResetOldGui)
{
CHECK(m_context != nullptr, ());
drape_ptr<gui::LayerRenderer> layerRenderer = m_guiCacher.RecacheWidgets(m_context, initInfo, m_texMng);
drape_ptr<Message> outputMsg = make_unique_dp<GuiLayerRecachedMessage>(std::move(layerRenderer), needResetOldGui);
m_commutator->PostMessage(ThreadsCommutator::RenderThread, std::move(outputMsg), MessagePriority::Normal);
}
#ifdef RENDER_DEBUG_INFO_LABELS
void BackendRenderer::RecacheDebugLabels()
{
CHECK(m_context != nullptr, ());
drape_ptr<gui::LayerRenderer> layerRenderer = m_guiCacher.RecacheDebugLabels(m_context, m_texMng);
drape_ptr<Message> outputMsg = make_unique_dp<GuiLayerRecachedMessage>(std::move(layerRenderer), false);
m_commutator->PostMessage(ThreadsCommutator::RenderThread, std::move(outputMsg), MessagePriority::Normal);
}
#endif
void BackendRenderer::RecacheChoosePositionMark()
{
CHECK(m_context != nullptr, ());
drape_ptr<gui::LayerRenderer> layerRenderer = m_guiCacher.RecacheChoosePositionMark(m_context, m_texMng);
drape_ptr<Message> outputMsg = make_unique_dp<GuiLayerRecachedMessage>(std::move(layerRenderer), false);
m_commutator->PostMessage(ThreadsCommutator::RenderThread, std::move(outputMsg), MessagePriority::Normal);
}
void BackendRenderer::AcceptMessage(ref_ptr<Message> message)
{
switch (message->GetType())
{
case Message::Type::UpdateReadManager:
{
TTilesCollection tiles = m_requestedTiles->GetTiles();
if (!tiles.empty())
{
ScreenBase screen;
bool have3dBuildings;
bool forceRequest;
bool forceUserMarksRequest;
m_requestedTiles->GetParams(screen, have3dBuildings, forceRequest, forceUserMarksRequest);
m_readManager->UpdateCoverage(screen, have3dBuildings, forceRequest, forceUserMarksRequest, tiles, m_texMng,
make_ref(m_metalineManager));
m_updateCurrentCountryFn(screen.ClipRect().Center(), (*tiles.begin()).m_zoomLevel);
}
break;
}
case Message::Type::InvalidateReadManagerRect:
{
ref_ptr<InvalidateReadManagerRectMessage> msg = message;
if (msg->NeedRestartReading())
m_readManager->Restart();
else
m_readManager->Invalidate(msg->GetTilesForInvalidate());
break;
}
case Message::Type::ShowChoosePositionMark:
{
RecacheChoosePositionMark();
break;
}
case Message::Type::GuiRecache:
{
ref_ptr<GuiRecacheMessage> msg = message;
m_lastWidgetsInfo = msg->GetInitInfo();
RecacheGui(m_lastWidgetsInfo, msg->NeedResetOldGui());
#ifdef RENDER_DEBUG_INFO_LABELS
RecacheDebugLabels();
#endif
break;
}
case Message::Type::GuiLayerLayout:
{
ref_ptr<GuiLayerLayoutMessage> msg = message;
m_commutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<GuiLayerLayoutMessage>(msg->AcceptLayoutInfo()), MessagePriority::Normal);
break;
}
case Message::Type::TileReadStarted:
{
ref_ptr<TileReadStartMessage> msg = message;
m_batchersPool->ReserveBatcher(msg->GetKey());
break;
}
case Message::Type::TileReadEnded:
{
ref_ptr<TileReadEndMessage> msg = message;
CHECK(m_context != nullptr, ());
m_batchersPool->ReleaseBatcher(m_context, msg->GetKey());
break;
}
case Message::Type::FinishTileRead:
{
ref_ptr<FinishTileReadMessage> msg = message;
CHECK(m_context != nullptr, ());
if (msg->NeedForceUpdateUserMarks())
for (auto const & tileKey : msg->GetTiles())
m_userMarkGenerator->GenerateUserMarksGeometry(m_context, tileKey, m_texMng);
m_commutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<FinishTileReadMessage>(msg->MoveTiles(), msg->NeedForceUpdateUserMarks()),
MessagePriority::Normal);
break;
}
case Message::Type::FinishReading:
{
TOverlaysRenderData overlays;
overlays.swap(m_overlays);
m_commutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<FlushOverlaysMessage>(std::move(overlays)), MessagePriority::Normal);
break;
}
case Message::Type::MapShapesRecache:
{
RecacheMapShapes();
break;
}
case Message::Type::MapShapeReaded:
{
ref_ptr<MapShapeReadedMessage> msg = message;
auto const & tileKey = msg->GetKey();
if (m_requestedTiles->CheckTileKey(tileKey) && m_readManager->CheckTileKey(tileKey))
{
CHECK(m_context != nullptr, ());
ref_ptr<dp::Batcher> batcher = m_batchersPool->GetBatcher(tileKey);
batcher->SetBatcherHash(tileKey.GetHashValue(BatcherBucket::Default));
#if defined(DRAPE_MEASURER_BENCHMARK) && defined(GENERATING_STATISTIC)
DrapeMeasurer::Instance().StartShapesGeneration();
#endif
for (drape_ptr<MapShape> const & shape : msg->GetShapes())
{
batcher->SetFeatureMinZoom(shape->GetFeatureMinZoom());
shape->Draw(m_context, batcher, m_texMng);
}
#if defined(DRAPE_MEASURER_BENCHMARK) && defined(GENERATING_STATISTIC)
DrapeMeasurer::Instance().EndShapesGeneration(static_cast<uint32_t>(msg->GetShapes().size()));
#endif
}
break;
}
case Message::Type::OverlayMapShapeReaded:
{
ref_ptr<OverlayMapShapeReadedMessage> msg = message;
auto const & tileKey = msg->GetKey();
if (m_requestedTiles->CheckTileKey(tileKey) && m_readManager->CheckTileKey(tileKey))
{
CHECK(m_context != nullptr, ());
CleanupOverlays(tileKey);
#if defined(DRAPE_MEASURER_BENCHMARK) && defined(GENERATING_STATISTIC)
DrapeMeasurer::Instance().StartOverlayShapesGeneration();
#endif
OverlayBatcher batcher(tileKey);
for (drape_ptr<MapShape> const & shape : msg->GetShapes())
batcher.Batch(m_context, shape, m_texMng);
TOverlaysRenderData renderData;
batcher.Finish(m_context, renderData);
if (!renderData.empty())
{
m_overlays.reserve(m_overlays.size() + renderData.size());
std::move(renderData.begin(), renderData.end(), back_inserter(m_overlays));
}
#if defined(DRAPE_MEASURER_BENCHMARK) && defined(GENERATING_STATISTIC)
DrapeMeasurer::Instance().EndOverlayShapesGeneration(static_cast<uint32_t>(msg->GetShapes().size()));
#endif
}
break;
}
case Message::Type::ChangeUserMarkGroupVisibility:
{
ref_ptr<ChangeUserMarkGroupVisibilityMessage> msg = message;
m_userMarkGenerator->SetGroupVisibility(msg->GetGroupId(), msg->IsVisible());
break;
}
case Message::Type::UpdateUserMarks:
{
ref_ptr<UpdateUserMarksMessage> msg = message;
m_userMarkGenerator->SetRemovedUserMarks(msg->AcceptRemovedIds());
m_userMarkGenerator->SetUserMarks(msg->AcceptMarkRenderParams());
m_userMarkGenerator->SetUserLines(msg->AcceptLineRenderParams());
m_userMarkGenerator->SetJustCreatedUserMarks(msg->AcceptJustCreatedIds());
break;
}
case Message::Type::UpdateUserMarkGroup:
{
ref_ptr<UpdateUserMarkGroupMessage> msg = message;
kml::MarkGroupId const groupId = msg->GetGroupId();
m_userMarkGenerator->SetGroup(groupId, msg->AcceptIds());
break;
}
case Message::Type::ClearUserMarkGroup:
{
ref_ptr<ClearUserMarkGroupMessage> msg = message;
m_userMarkGenerator->RemoveGroup(msg->GetGroupId());
break;
}
case Message::Type::InvalidateUserMarks:
{
m_commutator->PostMessage(ThreadsCommutator::RenderThread, make_unique_dp<InvalidateUserMarksMessage>(),
MessagePriority::Normal);
break;
}
case Message::Type::AddSubroute:
{
ref_ptr<AddSubrouteMessage> msg = message;
CHECK(m_context != nullptr, ());
m_routeBuilder->Build(m_context, msg->GetSubrouteId(), msg->GetSubroute(), m_texMng, msg->GetRecacheId());
break;
}
case Message::Type::CacheSubrouteArrows:
{
ref_ptr<CacheSubrouteArrowsMessage> msg = message;
CHECK(m_context != nullptr, ());
m_routeBuilder->BuildArrows(m_context, msg->GetSubrouteId(), msg->GetBorders(), m_texMng, msg->GetRecacheId());
break;
}
case Message::Type::RemoveSubroute:
{
ref_ptr<RemoveSubrouteMessage> msg = message;
m_routeBuilder->ClearRouteCache();
// We have to resend the message to FR, because it guaranties that
// RemoveSubroute will be processed after FlushSubrouteMessage.
m_commutator->PostMessage(
ThreadsCommutator::RenderThread,
make_unique_dp<RemoveSubrouteMessage>(msg->GetSegmentId(), msg->NeedDeactivateFollowing()),
MessagePriority::Normal);
break;
}
case Message::Type::SwitchMapStyle:
{
ref_ptr<SwitchMapStyleMessage> msg = message;
msg->FilterDependentMessages();
CHECK(m_context != nullptr, ());
m_texMng->OnSwitchMapStyle(m_context);
RecacheMapShapes();
RecacheGui(m_lastWidgetsInfo, false /* needResetOldGui */);
#ifdef RENDER_DEBUG_INFO_LABELS
RecacheDebugLabels();
#endif
m_trafficGenerator->InvalidateTexturesCache();
m_transitBuilder->RebuildSchemes(m_context, m_texMng);
// For Vulkan we initialize deferred cleaning up.
if (m_context->GetApiVersion() == dp::ApiVersion::Vulkan)
{
std::vector<drape_ptr<dp::HWTexture>> textures;
m_texMng->GetTexturesToCleanup(textures);
if (!textures.empty())
{
m_commutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<CleanupTexturesMessage>(std::move(textures)), MessagePriority::Normal);
}
}
break;
}
case Message::Type::CacheCirclesPack:
{
ref_ptr<CacheCirclesPackMessage> msg = message;
drape_ptr<CirclesPackRenderData> data = make_unique_dp<CirclesPackRenderData>();
data->m_pointsCount = msg->GetPointsCount();
CHECK(m_context != nullptr, ());
CirclesPackShape::Draw(m_context, *data.get());
m_commutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<FlushCirclesPackMessage>(std::move(data), msg->GetDestination()),
MessagePriority::Normal);
break;
}
case Message::Type::Allow3dBuildings:
{
ref_ptr<Allow3dBuildingsMessage> msg = message;
m_readManager->Allow3dBuildings(msg->Allow3dBuildings());
break;
}
case Message::Type::SetMapLangIndex:
{
ref_ptr<SetMapLangIndexMessage> msg = message;
m_readManager->SetMapLangIndex(msg->MapLangIndex());
break;
}
case Message::Type::RequestSymbolsSize:
{
ref_ptr<RequestSymbolsSizeMessage> msg = message;
auto const & symbols = msg->GetSymbols();
RequestSymbolsSizeMessage::Sizes sizes;
for (auto const & s : symbols)
{
dp::TextureManager::SymbolRegion region;
m_texMng->GetSymbolRegion(s, region);
sizes.insert(std::make_pair(s, region.GetPixelSize()));
}
msg->InvokeCallback(std::move(sizes));
break;
}
case Message::Type::EnableTraffic:
{
ref_ptr<EnableTrafficMessage> msg = message;
if (!msg->IsTrafficEnabled())
m_trafficGenerator->ClearCache();
m_readManager->SetTrafficEnabled(msg->IsTrafficEnabled());
m_commutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<EnableTrafficMessage>(msg->IsTrafficEnabled()), MessagePriority::Normal);
break;
}
case Message::Type::FlushTrafficGeometry:
{
ref_ptr<FlushTrafficGeometryMessage> msg = message;
auto const & tileKey = msg->GetKey();
if (m_requestedTiles->CheckTileKey(tileKey) && m_readManager->CheckTileKey(tileKey))
{
CHECK(m_context != nullptr, ());
m_trafficGenerator->FlushSegmentsGeometry(m_context, tileKey, msg->GetSegments(), m_texMng);
}
break;
}
case Message::Type::UpdateTraffic:
{
ref_ptr<UpdateTrafficMessage> msg = message;
m_trafficGenerator->UpdateColoring(msg->GetSegmentsColoring());
m_commutator->PostMessage(ThreadsCommutator::RenderThread, make_unique_dp<RegenerateTrafficMessage>(),
MessagePriority::Normal);
break;
}
case Message::Type::ClearTrafficData:
{
ref_ptr<ClearTrafficDataMessage> msg = message;
m_trafficGenerator->ClearCache(msg->GetMwmId());
m_commutator->PostMessage(ThreadsCommutator::RenderThread, make_unique_dp<ClearTrafficDataMessage>(msg->GetMwmId()),
MessagePriority::Normal);
break;
}
case Message::Type::SetSimplifiedTrafficColors:
{
ref_ptr<SetSimplifiedTrafficColorsMessage> msg = message;
m_trafficGenerator->SetSimplifiedColorSchemeEnabled(msg->IsSimplified());
m_trafficGenerator->InvalidateTexturesCache();
m_commutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<SetSimplifiedTrafficColorsMessage>(msg->IsSimplified()),
MessagePriority::Normal);
break;
}
case Message::Type::UpdateTransitScheme:
{
ref_ptr<UpdateTransitSchemeMessage> msg = message;
CHECK(m_context != nullptr, ());
m_transitBuilder->UpdateSchemes(m_context, msg->GetTransitDisplayInfos(), m_texMng);
break;
}
case Message::Type::RegenerateTransitScheme:
{
CHECK(m_context != nullptr, ());
m_transitBuilder->RebuildSchemes(m_context, m_texMng);
break;
}
case Message::Type::ClearTransitSchemeData:
{
ref_ptr<ClearTransitSchemeDataMessage> msg = message;
m_transitBuilder->Clear(msg->GetMwmId());
m_commutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<ClearTransitSchemeDataMessage>(msg->GetMwmId()), MessagePriority::Normal);
break;
}
case Message::Type::ClearAllTransitSchemeData:
{
m_transitBuilder->Clear();
m_commutator->PostMessage(ThreadsCommutator::RenderThread, make_unique_dp<ClearAllTransitSchemeDataMessage>(),
MessagePriority::Normal);
break;
}
case Message::Type::EnableTransitScheme:
{
ref_ptr<EnableTransitSchemeMessage> msg = message;
if (!msg->IsEnabled())
m_transitBuilder->Clear();
m_commutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<EnableTransitSchemeMessage>(msg->IsEnabled()), MessagePriority::Normal);
break;
}
case Message::Type::EnableIsolines:
{
ref_ptr<EnableIsolinesMessage> msg = message;
m_readManager->SetIsolinesEnabled(msg->IsEnabled());
m_commutator->PostMessage(ThreadsCommutator::RenderThread, make_unique_dp<EnableIsolinesMessage>(msg->IsEnabled()),
MessagePriority::Normal);
break;
}
case Message::Type::DrapeApiAddLines:
{
ref_ptr<DrapeApiAddLinesMessage> msg = message;
CHECK(m_context != nullptr, ());
std::vector<drape_ptr<DrapeApiRenderProperty>> properties;
m_drapeApiBuilder->BuildLines(m_context, msg->GetLines(), m_texMng, properties);
m_commutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<DrapeApiFlushMessage>(std::move(properties)), MessagePriority::Normal);
break;
}
case Message::Type::DrapeApiRemove:
{
ref_ptr<DrapeApiRemoveMessage> msg = message;
m_commutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<DrapeApiRemoveMessage>(msg->GetId(), msg->NeedRemoveAll()),
MessagePriority::Normal);
break;
}
case Message::Type::SetCustomFeatures:
{
ref_ptr<SetCustomFeaturesMessage> msg = message;
m_readManager->SetCustomFeatures(msg->AcceptFeatures());
m_commutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<SetTrackedFeaturesMessage>(m_readManager->GetCustomFeaturesArray()),
MessagePriority::Normal);
break;
}
case Message::Type::RemoveCustomFeatures:
{
ref_ptr<RemoveCustomFeaturesMessage> msg = message;
bool changed = false;
if (msg->NeedRemoveAll())
changed = m_readManager->RemoveAllCustomFeatures();
else
changed = m_readManager->RemoveCustomFeatures(msg->GetMwmId());
if (changed)
{
m_commutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<SetTrackedFeaturesMessage>(m_readManager->GetCustomFeaturesArray()),
MessagePriority::Normal);
}
break;
}
case Message::Type::NotifyRenderThread:
{
ref_ptr<NotifyRenderThreadMessage> msg = message;
msg->InvokeFunctor();
break;
}
case Message::Type::CheckSelectionGeometry:
{
ref_ptr<CheckSelectionGeometryMessage> msg = message;
auto renderNode = SelectionShapeGenerator::GenerateSelectionGeometry(
m_context, msg->GetFeature(), m_texMng, make_ref(m_metalineManager), m_readManager->GetMapDataProvider());
if (renderNode && renderNode->GetBoundingBox().IsValid())
{
m_commutator->PostMessage(
ThreadsCommutator::RenderThread,
make_unique_dp<FlushSelectionGeometryMessage>(std::move(renderNode), msg->GetRecacheId()),
MessagePriority::Normal);
}
break;
}
case Message::Type::Arrow3dRecache:
{
ref_ptr<Arrow3dRecacheMessage> msg = message;
m_arrow3dCustomDecl = msg->GetArrow3dCustomDecl();
// Invalidate Arrow3d texture.
CHECK(m_context != nullptr, ());
m_texMng->InvalidateArrowTexture(
m_context, m_arrow3dCustomDecl ? m_arrow3dCustomDecl->m_arrowMeshTexturePath : std::string(),
m_arrow3dCustomDecl ? m_arrow3dCustomDecl->m_loadFromDefaultResourceFolder : false);
// Preload mesh data.
m_arrow3dPreloadedData = Arrow3d::PreloadMesh(m_arrow3dCustomDecl, m_texMng);
if (!m_arrow3dPreloadedData.m_meshData.has_value())
{
// Fallback to default arrow mesh.
m_arrow3dCustomDecl.reset();
m_texMng->InvalidateArrowTexture(m_context);
m_arrow3dPreloadedData = Arrow3d::PreloadMesh(m_arrow3dCustomDecl, m_texMng);
}
// Recache map shapes.
RecacheMapShapes();
// For Vulkan we initialize deferred cleaning up.
if (m_context->GetApiVersion() == dp::ApiVersion::Vulkan)
{
std::vector<drape_ptr<dp::HWTexture>> textures;
m_texMng->GetTexturesToCleanup(textures);
if (!textures.empty())
{
m_commutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<CleanupTexturesMessage>(std::move(textures)), MessagePriority::Normal);
}
}
break;
}
#if defined(OMIM_OS_DESKTOP)
case Message::Type::NotifyGraphicsReady:
{
ref_ptr<NotifyGraphicsReadyMessage> msg = message;
m_commutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<NotifyGraphicsReadyMessage>(msg->GetCallback(), msg->NeedInvalidate()),
MessagePriority::Normal);
break;
}
#endif
default: ASSERT(false, ()); break;
}
}
void BackendRenderer::ReleaseResources()
{
m_readManager->Stop();
m_readManager.reset();
m_metalineManager.reset();
m_batchersPool.reset();
m_routeBuilder.reset();
m_overlays.clear();
m_trafficGenerator.reset();
m_texMng->Release();
// Here m_context can be nullptr, so call the method
// for the context from the factory.
m_contextFactory->GetResourcesUploadContext()->DoneCurrent();
m_context = nullptr;
}
void BackendRenderer::OnContextCreate()
{
LOG(LINFO, ("On context create."));
m_context = m_contextFactory->GetResourcesUploadContext();
m_contextFactory->WaitForInitialization(m_context.get());
m_context->MakeCurrent();
m_context->Init(m_apiVersion);
dp::SupportManager::Instance().Init(m_context);
m_readManager->Start();
InitContextDependentResources();
}
void BackendRenderer::OnContextDestroy()
{
LOG(LINFO, ("On context destroy."));
m_readManager->Stop();
m_batchersPool.reset();
m_metalineManager->Stop();
m_texMng->Release();
m_overlays.clear();
m_trafficGenerator->ClearContextDependentResources();
// Here we have to erase weak pointer to the context, since it
// can be destroyed after this method.
CHECK(m_context != nullptr, ());
m_context->DoneCurrent();
m_context = nullptr;
}
BackendRenderer::Routine::Routine(BackendRenderer & renderer) : m_renderer(renderer) {}
void BackendRenderer::Routine::Do()
{
LOG(LINFO, ("Start routine."));
m_renderer.CreateContext();
while (!IsCancelled())
m_renderer.IterateRenderLoop();
m_renderer.ReleaseResources();
}
void BackendRenderer::RenderFrame()
{
CHECK(m_context != nullptr, ());
if (!m_context->Validate())
return;
ProcessSingleMessage();
m_context->CollectMemory();
}
void BackendRenderer::InitContextDependentResources()
{
// Increase this value for big features.
uint32_t constexpr kBatchSize = 5000;
m_batchersPool = make_unique_dp<BatchersPool<TileKey, TileKeyStrictComparator>>(
kReadingThreadsCount, std::bind(&BackendRenderer::FlushGeometry, this, _1, _2, _3), kBatchSize, kBatchSize);
m_trafficGenerator->Init();
dp::TextureManager::Params params;
params.m_resPostfix = VisualParams::Instance().GetResourcePostfix();
params.m_visualScale = df::VisualParams::Instance().GetVisualScale();
#ifdef BUILD_DESIGNER
params.m_colors = "colors_design.txt";
params.m_patterns = "patterns_design.txt";
#else
params.m_colors = "colors.txt";
params.m_patterns = "patterns.txt";
#endif // BUILD_DESIGNER
params.m_glyphMngParams.m_uniBlocks = base::JoinPath("fonts", "unicode_blocks.txt");
params.m_glyphMngParams.m_whitelist = base::JoinPath("fonts", "whitelist.txt");
params.m_glyphMngParams.m_blacklist = base::JoinPath("fonts", "blacklist.txt");
GetPlatform().GetFontNames(params.m_glyphMngParams.m_fonts);
if (m_arrow3dCustomDecl)
{
params.m_arrowTexturePath = m_arrow3dCustomDecl->m_arrowMeshTexturePath;
params.m_arrowTextureUseDefaultResourceFolder = m_arrow3dCustomDecl->m_loadFromDefaultResourceFolder;
}
CHECK(m_context != nullptr, ());
m_texMng->Init(m_context, params);
// Send some textures to frontend renderer.
drape_ptr<PostprocessStaticTextures> textures = make_unique_dp<PostprocessStaticTextures>();
textures->m_smaaAreaTexture = m_texMng->GetSMAAAreaTexture();
textures->m_smaaSearchTexture = m_texMng->GetSMAASearchTexture();
m_commutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<SetPostprocessStaticTexturesMessage>(std::move(textures)),
MessagePriority::Normal);
m_commutator->PostMessage(ThreadsCommutator::RenderThread, make_unique_dp<FinishTexturesInitializationMessage>(),
MessagePriority::Normal);
// Preload Arrow3D mesh.
m_arrow3dPreloadedData = Arrow3d::PreloadMesh(m_arrow3dCustomDecl, m_texMng);
}
void BackendRenderer::RecacheMapShapes()
{
CHECK(m_context != nullptr, ());
auto msg =
make_unique_dp<MapShapesMessage>(make_unique_dp<MyPosition>(m_context, m_texMng),
make_unique_dp<SelectionShape>(m_context, m_texMng), m_arrow3dPreloadedData);
m_context->Flush();
m_commutator->PostMessage(ThreadsCommutator::RenderThread, std::move(msg), MessagePriority::Normal);
}
void BackendRenderer::FlushGeometry(TileKey const & key, dp::RenderState const & state,
drape_ptr<dp::RenderBucket> && buffer)
{
CHECK(m_context != nullptr, ());
m_context->Flush();
m_commutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<FlushRenderBucketMessage>(key, state, std::move(buffer)),
MessagePriority::Normal);
}
void BackendRenderer::FlushTransitRenderData(TransitRenderData && renderData)
{
m_commutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<FlushTransitSchemeMessage>(std::move(renderData)), MessagePriority::Normal);
}
void BackendRenderer::FlushTrafficRenderData(TrafficRenderData && renderData)
{
m_commutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<FlushTrafficDataMessage>(std::move(renderData)), MessagePriority::Normal);
}
void BackendRenderer::FlushUserMarksRenderData(TUserMarksRenderData && renderData)
{
m_commutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<FlushUserMarksMessage>(std::move(renderData)), MessagePriority::Normal);
}
void BackendRenderer::CleanupOverlays(TileKey const & tileKey)
{
auto const functor = [&tileKey](OverlayRenderData const & data)
{ return data.m_tileKey == tileKey && data.m_tileKey.m_generation < tileKey.m_generation; };
m_overlays.erase(std::remove_if(m_overlays.begin(), m_overlays.end(), functor), m_overlays.end());
}
} // namespace df

View file

@ -0,0 +1,142 @@
#pragma once
#include "drape_frontend/arrow3d.hpp"
#include "drape_frontend/base_renderer.hpp"
#include "drape_frontend/batchers_pool.hpp"
#include "drape_frontend/drape_api_builder.hpp"
#include "drape_frontend/drape_engine_params.hpp"
#include "drape_frontend/gui/layer_render.hpp"
#include "drape_frontend/map_data_provider.hpp"
#include "drape_frontend/overlay_batcher.hpp"
#include "drape_frontend/requested_tiles.hpp"
#include "drape_frontend/traffic_generator.hpp"
#include "drape_frontend/transit_scheme_builder.hpp"
#include "drape_frontend/user_mark_generator.hpp"
#include "drape/pointers.hpp"
#include "drape/viewport.hpp"
#include <functional>
#include <memory>
namespace dp
{
class GraphicsContextFactory;
class TextureManager;
} // namespace dp
namespace df
{
class Message;
class ReadManager;
class RouteBuilder;
class MetalineManager;
class BackendRenderer : public BaseRenderer
{
public:
using TUpdateCurrentCountryFn = std::function<void(m2::PointD const &, int)>;
struct Params : BaseRenderer::Params
{
Params(dp::ApiVersion apiVersion, ref_ptr<ThreadsCommutator> commutator,
ref_ptr<dp::GraphicsContextFactory> factory, ref_ptr<dp::TextureManager> texMng,
MapDataProvider const & model, TUpdateCurrentCountryFn const & updateCurrentCountryFn,
ref_ptr<RequestedTiles> requestedTiles, bool allow3dBuildings, bool trafficEnabled, bool isolinesEnabled,
bool simplifiedTrafficColors, std::optional<Arrow3dCustomDecl> arrow3dCustomDecl,
OnGraphicsContextInitialized const & onGraphicsContextInitialized)
: BaseRenderer::Params(apiVersion, commutator, factory, texMng, onGraphicsContextInitialized)
, m_model(model)
, m_updateCurrentCountryFn(updateCurrentCountryFn)
, m_requestedTiles(requestedTiles)
, m_allow3dBuildings(allow3dBuildings)
, m_trafficEnabled(trafficEnabled)
, m_isolinesEnabled(isolinesEnabled)
, m_simplifiedTrafficColors(simplifiedTrafficColors)
, m_arrow3dCustomDecl(std::move(arrow3dCustomDecl))
{}
MapDataProvider const & m_model;
TUpdateCurrentCountryFn m_updateCurrentCountryFn;
ref_ptr<RequestedTiles> m_requestedTiles;
bool m_allow3dBuildings;
bool m_trafficEnabled;
bool m_isolinesEnabled;
bool m_simplifiedTrafficColors;
std::optional<Arrow3dCustomDecl> m_arrow3dCustomDecl;
};
explicit BackendRenderer(Params && params);
~BackendRenderer() override;
void Teardown();
protected:
std::unique_ptr<threads::IRoutine> CreateRoutine() override;
void RenderFrame() override;
void OnContextCreate() override;
void OnContextDestroy() override;
private:
void RecacheGui(gui::TWidgetsInitInfo const & initInfo, bool needResetOldGui);
void RecacheChoosePositionMark();
void RecacheMapShapes();
#ifdef RENDER_DEBUG_INFO_LABELS
void RecacheDebugLabels();
#endif
void AcceptMessage(ref_ptr<Message> message) override;
class Routine : public threads::IRoutine
{
public:
explicit Routine(BackendRenderer & renderer);
void Do() override;
private:
BackendRenderer & m_renderer;
};
void ReleaseResources();
void InitContextDependentResources();
void FlushGeometry(TileKey const & key, dp::RenderState const & state, drape_ptr<dp::RenderBucket> && buffer);
void FlushTransitRenderData(TransitRenderData && renderData);
void FlushTrafficRenderData(TrafficRenderData && renderData);
void FlushUserMarksRenderData(TUserMarksRenderData && renderData);
void CleanupOverlays(TileKey const & tileKey);
MapDataProvider m_model;
drape_ptr<BatchersPool<TileKey, TileKeyStrictComparator>> m_batchersPool;
drape_ptr<ReadManager> m_readManager;
drape_ptr<RouteBuilder> m_routeBuilder;
drape_ptr<TransitSchemeBuilder> m_transitBuilder;
drape_ptr<TrafficGenerator> m_trafficGenerator;
drape_ptr<UserMarkGenerator> m_userMarkGenerator;
drape_ptr<DrapeApiBuilder> m_drapeApiBuilder;
gui::LayerCacher m_guiCacher;
ref_ptr<RequestedTiles> m_requestedTiles;
TOverlaysRenderData m_overlays;
TUpdateCurrentCountryFn m_updateCurrentCountryFn;
drape_ptr<MetalineManager> m_metalineManager;
gui::TWidgetsInitInfo m_lastWidgetsInfo;
std::optional<Arrow3dCustomDecl> m_arrow3dCustomDecl;
Arrow3d::PreloadedData m_arrow3dPreloadedData;
#ifdef DEBUG
bool m_isTeardowned;
#endif
};
} // namespace df

View file

@ -0,0 +1,227 @@
#include "drape_frontend/base_renderer.hpp"
#include "drape_frontend/message_subclasses.hpp"
#include <utility>
#if defined(OMIM_METAL_AVAILABLE)
namespace dp
{
extern void RenderFrameMediator(std::function<void()> && renderFrameFunction);
} // namespace dp
#define RENDER_FRAME_MEDIATOR(renderFunction) dp::RenderFrameMediator([this] { renderFunction; })
#else
#define RENDER_FRAME_MEDIATOR(renderFunction) renderFunction
#endif
namespace df
{
// static
std::atomic<uint8_t> BaseRenderer::m_contextCounter(0);
BaseRenderer::BaseRenderer(ThreadsCommutator::ThreadName name, Params const & params)
: m_apiVersion(params.m_apiVersion)
, m_commutator(params.m_commutator)
, m_contextFactory(params.m_oglContextFactory)
, m_texMng(params.m_texMng)
, m_threadName(name)
, m_isEnabled(true)
, m_renderingEnablingCompletionHandler(nullptr)
, m_wasNotified(false)
, m_wasContextReset(false)
, m_onGraphicsContextInitialized(params.m_onGraphicsContextInitialized)
{
m_commutator->RegisterThread(m_threadName, this);
}
void BaseRenderer::StartThread()
{
m_selfThread.Create(CreateRoutine());
}
void BaseRenderer::StopThread()
{
// stop rendering and close queue
m_selfThread.GetRoutine()->Cancel();
CloseQueue();
// wake up render thread if necessary
if (!m_isEnabled)
WakeUp();
// wait for render thread completion
m_selfThread.Join();
}
void BaseRenderer::IterateRenderLoop()
{
RENDER_FRAME_MEDIATOR(IterateRenderLoopImpl());
}
void BaseRenderer::IterateRenderLoopImpl()
{
RenderFrame();
CheckRenderingEnabled();
}
void BaseRenderer::SetRenderingEnabled(ref_ptr<dp::GraphicsContextFactory> contextFactory)
{
if (m_wasContextReset && contextFactory != nullptr)
m_contextFactory = contextFactory;
SetRenderingEnabled(true);
}
void BaseRenderer::SetRenderingDisabled(bool const destroySurface)
{
if (destroySurface)
m_wasContextReset = true;
SetRenderingEnabled(false);
}
bool BaseRenderer::IsRenderingEnabled() const
{
return m_isEnabled;
}
void BaseRenderer::SetRenderingEnabled(bool const isEnabled)
{
if (isEnabled == m_isEnabled)
return;
// here we have to wait for completion of internal SetRenderingEnabled
std::mutex completionMutex;
std::condition_variable completionCondition;
bool notified = false;
auto handler = [&]()
{
std::lock_guard<std::mutex> lock(completionMutex);
notified = true;
completionCondition.notify_one();
};
{
std::lock_guard<std::mutex> lock(m_completionHandlerMutex);
m_renderingEnablingCompletionHandler = std::move(handler);
}
if (isEnabled)
{
// wake up rendering thread
WakeUp();
}
else
{
// here we set up value only if rendering disabled
m_isEnabled = false;
// if renderer thread is waiting for message let it go
CancelMessageWaiting();
}
std::unique_lock<std::mutex> lock(completionMutex);
completionCondition.wait(lock, [&notified] { return notified; });
}
bool BaseRenderer::FilterContextDependentMessage(ref_ptr<Message> msg)
{
return msg->IsGraphicsContextDependent();
}
void BaseRenderer::CreateContext()
{
OnContextCreate();
m_contextCounter++;
uint8_t constexpr kContextCount = 2;
if (m_contextCounter == kContextCount && m_onGraphicsContextInitialized)
m_onGraphicsContextInitialized();
}
void BaseRenderer::CheckRenderingEnabled()
{
if (!m_isEnabled)
{
dp::GraphicsContext * context = nullptr;
if (m_wasContextReset)
{
using namespace std::placeholders;
EnableMessageFiltering(std::bind(&BaseRenderer::FilterContextDependentMessage, this, _1));
OnContextDestroy();
CHECK(m_contextCounter > 0, ());
m_contextCounter--;
}
else
{
bool const isDrawContext = m_threadName == ThreadsCommutator::RenderThread;
context = isDrawContext ? m_contextFactory->GetDrawContext() : m_contextFactory->GetResourcesUploadContext();
context->SetRenderingEnabled(false);
}
OnRenderingDisabled();
// notify initiator-thread about rendering disabling
Notify();
// wait for signal
std::unique_lock<std::mutex> lock(m_renderingEnablingMutex);
m_renderingEnablingCondition.wait(lock, [this] { return m_wasNotified; });
m_wasNotified = false;
bool needCreateContext = false;
if (!m_selfThread.GetRoutine()->IsCancelled())
{
// here rendering is enabled again
m_isEnabled = true;
if (m_wasContextReset)
{
m_wasContextReset = false;
needCreateContext = true;
}
else
{
context->SetRenderingEnabled(true);
}
}
if (needCreateContext)
DisableMessageFiltering();
// notify initiator-thread about rendering enabling
// m_renderingEnablingCompletionHandler will be setup before awakening of this thread
Notify();
OnRenderingEnabled();
if (needCreateContext)
CreateContext();
}
}
void BaseRenderer::Notify()
{
std::function<void()> handler;
{
std::lock_guard<std::mutex> lock(m_completionHandlerMutex);
handler = std::move(m_renderingEnablingCompletionHandler);
m_renderingEnablingCompletionHandler = nullptr;
}
if (handler != nullptr)
handler();
}
void BaseRenderer::WakeUp()
{
std::lock_guard<std::mutex> lock(m_renderingEnablingMutex);
m_wasNotified = true;
m_renderingEnablingCondition.notify_one();
}
bool BaseRenderer::CanReceiveMessages()
{
threads::IRoutine * routine = m_selfThread.GetRoutine();
return routine != nullptr && !routine->IsCancelled();
}
} // namespace df

View file

@ -0,0 +1,105 @@
#pragma once
#include "drape_frontend/message_acceptor.hpp"
#include "drape_frontend/threads_commutator.hpp"
#include "drape_frontend/tile_utils.hpp"
#include "drape/graphics_context_factory.hpp"
#include "drape/texture_manager.hpp"
#include "base/thread.hpp"
#include <atomic>
#include <condition_variable>
#include <functional>
#include <memory>
#include <mutex>
namespace df
{
using OnGraphicsContextInitialized = std::function<void()>;
class BaseRenderer : public MessageAcceptor
{
public:
struct Params
{
Params(dp::ApiVersion apiVersion, ref_ptr<ThreadsCommutator> commutator,
ref_ptr<dp::GraphicsContextFactory> factory, ref_ptr<dp::TextureManager> texMng,
OnGraphicsContextInitialized const & onGraphicsContextInitialized)
: m_apiVersion(apiVersion)
, m_commutator(commutator)
, m_oglContextFactory(factory)
, m_texMng(texMng)
, m_onGraphicsContextInitialized(onGraphicsContextInitialized)
{}
dp::ApiVersion m_apiVersion;
ref_ptr<ThreadsCommutator> m_commutator;
ref_ptr<dp::GraphicsContextFactory> m_oglContextFactory;
ref_ptr<dp::TextureManager> m_texMng;
OnGraphicsContextInitialized m_onGraphicsContextInitialized;
};
BaseRenderer(ThreadsCommutator::ThreadName name, Params const & params);
bool CanReceiveMessages();
void IterateRenderLoop();
void SetRenderingEnabled(ref_ptr<dp::GraphicsContextFactory> contextFactory);
void SetRenderingDisabled(bool const destroySurface);
bool IsRenderingEnabled() const;
dp::ApiVersion GetApiVersion() const { return m_apiVersion; }
protected:
dp::ApiVersion m_apiVersion;
ref_ptr<ThreadsCommutator> m_commutator;
ref_ptr<dp::GraphicsContextFactory> m_contextFactory;
ref_ptr<dp::GraphicsContext> m_context;
ref_ptr<dp::TextureManager> m_texMng;
void StartThread();
void StopThread();
void CreateContext();
void CheckRenderingEnabled();
virtual std::unique_ptr<threads::IRoutine> CreateRoutine() = 0;
virtual void RenderFrame() = 0;
virtual void OnContextCreate() = 0;
virtual void OnContextDestroy() = 0;
virtual void OnRenderingEnabled() {}
virtual void OnRenderingDisabled() {}
private:
using TCompletionHandler = std::function<void()>;
void IterateRenderLoopImpl();
threads::Thread m_selfThread;
ThreadsCommutator::ThreadName m_threadName;
std::mutex m_renderingEnablingMutex;
std::condition_variable m_renderingEnablingCondition;
std::atomic<bool> m_isEnabled;
TCompletionHandler m_renderingEnablingCompletionHandler;
std::mutex m_completionHandlerMutex;
bool m_wasNotified;
std::atomic<bool> m_wasContextReset;
OnGraphicsContextInitialized m_onGraphicsContextInitialized;
static std::atomic<uint8_t> m_contextCounter;
bool FilterContextDependentMessage(ref_ptr<Message> msg);
void SetRenderingEnabled(bool const isEnabled);
void Notify();
void WakeUp();
};
} // namespace df

View file

@ -0,0 +1,15 @@
#pragma once
namespace df
{
// Now only [0-7] values are available.
enum class BatcherBucket
{
Default = 0,
Overlay = 1,
UserMark = 2,
Routing = 3,
Traffic = 4,
Transit = 5
};
} // namespace df

View file

@ -0,0 +1,82 @@
#pragma once
#include "drape/batcher.hpp"
#include "drape/object_pool.hpp"
#include "base/assert.hpp"
#include <functional>
#include <map>
#include <utility>
namespace df
{
// Not thread safe
template <typename TKey, typename TKeyComparator>
class BatchersPool final
{
public:
using TFlushFn =
std::function<void(TKey const & key, dp::RenderState const & state, drape_ptr<dp::RenderBucket> && buffer)>;
BatchersPool(int initBatchersCount, TFlushFn const & flushFn, uint32_t indexBufferSize, uint32_t vertexBufferSize)
: m_flushFn(flushFn)
, m_pool(initBatchersCount, dp::BatcherFactory(indexBufferSize, vertexBufferSize))
{}
~BatchersPool()
{
for (auto const & p : m_batchers)
{
dp::Batcher * batcher = p.second.first;
batcher->ResetSession();
m_pool.Return(batcher);
}
m_batchers.clear();
}
void ReserveBatcher(TKey const & key)
{
auto it = m_batchers.find(key);
if (it != m_batchers.end())
{
it->second.second++;
return;
}
dp::Batcher * batcher = m_pool.Get();
using namespace std::placeholders;
m_batchers.insert(std::make_pair(key, std::make_pair(batcher, 1)));
batcher->StartSession(std::bind(m_flushFn, key, _1, _2));
}
ref_ptr<dp::Batcher> GetBatcher(TKey const & key)
{
auto it = m_batchers.find(key);
CHECK(it != m_batchers.end(), ());
return make_ref(it->second.first);
}
void ReleaseBatcher(ref_ptr<dp::GraphicsContext> context, TKey const & key)
{
auto it = m_batchers.find(key);
ASSERT(it != m_batchers.end(), ());
ASSERT_GREATER(it->second.second, 0, ());
if ((--it->second.second) == 0)
{
dp::Batcher * batcher = it->second.first;
batcher->EndSession(context);
m_pool.Return(batcher);
m_batchers.erase(it);
}
}
private:
using TBatcherPair = std::pair<dp::Batcher *, int>;
using TBatcherMap = std::map<TKey, TBatcherPair, TKeyComparator>;
TFlushFn m_flushFn;
dp::ObjectPool<dp::Batcher, dp::BatcherFactory> m_pool;
TBatcherMap m_batchers;
};
} // namespace df

View file

@ -0,0 +1,179 @@
#include "drape_frontend/circles_pack_shape.hpp"
#include "drape_frontend/batcher_bucket.hpp"
#include "shaders/programs.hpp"
#include "drape/attribute_provider.hpp"
#include "drape/batcher.hpp"
#include "drape/glsl_func.hpp"
#include "drape/glsl_types.hpp"
#include "drape/graphics_context.hpp"
#include "drape/texture_manager.hpp"
#include <memory>
namespace df
{
namespace
{
uint32_t constexpr kDynamicStreamID = 0x7F;
struct CirclesPackStaticVertex
{
using TNormal = glsl::vec3;
CirclesPackStaticVertex() = default;
explicit CirclesPackStaticVertex(TNormal const & normal) : m_normal(normal) {}
TNormal m_normal;
};
dp::RenderState GetCirclesPackState()
{
auto state = CreateRenderState(gpu::Program::CirclePoint, DepthLayer::OverlayLayer);
state.SetDepthTestEnabled(false);
return state;
}
dp::BindingInfo const & GetCirclesPackStaticBindingInfo()
{
static std::unique_ptr<dp::BindingInfo> s_info;
if (s_info == nullptr)
{
dp::BindingFiller<CirclesPackStaticVertex> filler(1);
filler.FillDecl<CirclesPackStaticVertex::TNormal>("a_normal");
s_info.reset(new dp::BindingInfo(filler.m_info));
}
return *s_info;
}
dp::BindingInfo const & GetCirclesPackDynamicBindingInfo()
{
static std::unique_ptr<dp::BindingInfo> s_info;
if (s_info == nullptr)
{
dp::BindingFiller<CirclesPackDynamicVertex> filler(2, kDynamicStreamID);
filler.FillDecl<CirclesPackDynamicVertex::TPosition>("a_position");
filler.FillDecl<CirclesPackDynamicVertex::TColor>("a_color");
s_info.reset(new dp::BindingInfo(filler.m_info));
}
return *s_info;
}
} // namespace
CirclesPackHandle::CirclesPackHandle(size_t pointsCount)
: OverlayHandle(dp::OverlayID{}, dp::Anchor::Center, 0 /* priority */, 1 /* minVisibleScale */,
false /* isBillboard */)
, m_needUpdate(false)
{
m_buffer.resize(pointsCount * dp::Batcher::VertexPerQuad);
}
void CirclesPackHandle::GetAttributeMutation(ref_ptr<dp::AttributeBufferMutator> mutator) const
{
if (!m_needUpdate)
return;
TOffsetNode const & node = GetOffsetNode(kDynamicStreamID);
ASSERT_EQUAL(node.first.GetElementSize(), sizeof(CirclesPackDynamicVertex), ());
ASSERT_EQUAL(node.second.m_count, m_buffer.size(), ());
uint32_t const bytesCount = static_cast<uint32_t>(m_buffer.size()) * sizeof(CirclesPackDynamicVertex);
void * buffer = mutator->AllocateMutationBuffer(bytesCount);
memcpy(buffer, m_buffer.data(), bytesCount);
dp::MutateNode mutateNode;
mutateNode.m_region = node.second;
mutateNode.m_data = make_ref(buffer);
mutator->AddMutation(node.first, mutateNode);
m_needUpdate = false;
}
bool CirclesPackHandle::Update(ScreenBase const & screen)
{
UNUSED_VALUE(screen);
return true;
}
bool CirclesPackHandle::IndexesRequired() const
{
return false;
}
m2::RectD CirclesPackHandle::GetPixelRect(ScreenBase const & screen, bool perspective) const
{
UNUSED_VALUE(screen);
UNUSED_VALUE(perspective);
return m2::RectD();
}
void CirclesPackHandle::GetPixelShape(ScreenBase const & screen, bool perspective, Rects & rects) const
{
UNUSED_VALUE(screen);
UNUSED_VALUE(perspective);
}
void CirclesPackHandle::SetPoint(size_t index, m2::PointD const & position, float radius, dp::Color const & color)
{
size_t const bufferIndex = index * dp::Batcher::VertexPerQuad;
ASSERT_LESS_OR_EQUAL(bufferIndex + dp::Batcher::VertexPerQuad, m_buffer.size(), ());
for (size_t i = 0; i < dp::Batcher::VertexPerQuad; ++i)
{
m_buffer[bufferIndex + i].m_position = glsl::vec3(position.x, position.y, radius);
m_buffer[bufferIndex + i].m_color = glsl::ToVec4(color);
}
m_needUpdate = true;
}
void CirclesPackHandle::Clear()
{
fill(begin(m_buffer), end(m_buffer), CirclesPackDynamicVertex(glsl::vec3(0.0f), glsl::vec4(0.0f)));
m_needUpdate = true;
}
size_t CirclesPackHandle::GetPointsCount() const
{
return m_buffer.size() / dp::Batcher::VertexPerQuad;
}
void CirclesPackShape::Draw(ref_ptr<dp::GraphicsContext> context, CirclesPackRenderData & data)
{
ASSERT_NOT_EQUAL(data.m_pointsCount, 0, ());
uint32_t constexpr kVerticesInPoint = dp::Batcher::VertexPerQuad;
uint32_t constexpr kIndicesInPoint = dp::Batcher::IndexPerQuad;
std::vector<CirclesPackStaticVertex> staticVertexData;
staticVertexData.reserve(data.m_pointsCount * kVerticesInPoint);
static_assert(kVerticesInPoint == 4, "According to the loop below");
for (size_t i = 0; i < data.m_pointsCount; ++i)
{
staticVertexData.emplace_back(CirclesPackStaticVertex::TNormal(-1.0f, 1.0f, 1.0f));
staticVertexData.emplace_back(CirclesPackStaticVertex::TNormal(-1.0f, -1.0f, 1.0f));
staticVertexData.emplace_back(CirclesPackStaticVertex::TNormal(1.0f, 1.0f, 1.0f));
staticVertexData.emplace_back(CirclesPackStaticVertex::TNormal(1.0f, -1.0f, 1.0f));
}
std::vector<CirclesPackDynamicVertex> dynamicVertexData;
dynamicVertexData.resize(data.m_pointsCount * kVerticesInPoint);
dp::Batcher batcher(data.m_pointsCount * kIndicesInPoint, data.m_pointsCount * kVerticesInPoint);
batcher.SetBatcherHash(static_cast<uint64_t>(BatcherBucket::Overlay));
dp::SessionGuard guard(context, batcher, [&data](dp::RenderState const & state, drape_ptr<dp::RenderBucket> && b)
{
data.m_bucket = std::move(b);
data.m_state = state;
});
drape_ptr<dp::OverlayHandle> handle = make_unique_dp<CirclesPackHandle>(data.m_pointsCount);
dp::AttributeProvider provider(2 /* stream count */, static_cast<uint32_t>(staticVertexData.size()));
provider.InitStream(0 /* stream index */, GetCirclesPackStaticBindingInfo(), make_ref(staticVertexData.data()));
provider.InitStream(1 /* stream index */, GetCirclesPackDynamicBindingInfo(), make_ref(dynamicVertexData.data()));
batcher.InsertListOfStrip(context, GetCirclesPackState(), make_ref(&provider), std::move(handle), kVerticesInPoint);
context->Flush();
}
} // namespace df

View file

@ -0,0 +1,72 @@
#pragma once
#include "drape_frontend/map_shape.hpp"
#include "drape_frontend/render_state_extension.hpp"
#include "drape_frontend/shape_view_params.hpp"
#include "drape/overlay_handle.hpp"
#include "drape/pointers.hpp"
#include "drape/render_bucket.hpp"
#include "drape/utils/vertex_decl.hpp"
#include <vector>
namespace dp
{
class TextureManager;
class GraphicsContext;
} // namespace dp
namespace df
{
struct CirclesPackRenderData
{
uint32_t m_pointsCount;
dp::RenderState m_state;
drape_ptr<dp::RenderBucket> m_bucket;
CirclesPackRenderData()
: m_pointsCount(0)
, m_state(CreateRenderState(gpu::Program::CirclePoint, DepthLayer::OverlayLayer))
{}
};
struct CirclesPackDynamicVertex
{
using TPosition = glsl::vec3;
using TColor = glsl::vec4;
CirclesPackDynamicVertex() = default;
CirclesPackDynamicVertex(TPosition const & pos, TColor const & color) : m_position(pos), m_color(color) {}
TPosition m_position;
TColor m_color;
};
class CirclesPackHandle : public dp::OverlayHandle
{
using TBase = dp::OverlayHandle;
public:
explicit CirclesPackHandle(size_t pointsCount);
void GetAttributeMutation(ref_ptr<dp::AttributeBufferMutator> mutator) const override;
bool Update(ScreenBase const & screen) override;
m2::RectD GetPixelRect(ScreenBase const & screen, bool perspective) const override;
void GetPixelShape(ScreenBase const & screen, bool perspective, Rects & rects) const override;
bool IndexesRequired() const override;
void Clear();
void SetPoint(size_t index, m2::PointD const & position, float radius, dp::Color const & color);
size_t GetPointsCount() const;
private:
std::vector<CirclesPackDynamicVertex> m_buffer;
mutable bool m_needUpdate;
};
class CirclesPackShape
{
public:
static void Draw(ref_ptr<dp::GraphicsContext> context, CirclesPackRenderData & data);
};
} // namespace df

View file

@ -0,0 +1,140 @@
#include "drape_frontend/color_constants.hpp"
#include "drape_frontend/apply_feature_functors.hpp"
#include "platform/platform.hpp"
#include "indexer/drawing_rules.hpp"
#include "indexer/map_style_reader.hpp"
#include "coding/reader.hpp"
#include "base/assert.hpp"
#include "base/string_utils.hpp"
#include "cppjansson/cppjansson.hpp"
#include <fstream>
namespace
{
std::string const kTransitColorFileName = "transit_colors.txt";
class TransitColorsHolder
{
public:
dp::Color GetColor(std::string const & name) const
{
auto const style = GetStyleReader().GetCurrentStyle();
auto const isDarkStyle = style == MapStyle::MapStyleDefaultDark || style == MapStyle::MapStyleVehicleDark;
auto const & colors = isDarkStyle ? m_nightColors : m_clearColors;
auto const it = colors.find(name);
if (it == colors.cend())
return dp::Color();
return it->second;
}
void Load()
{
std::string data;
try
{
ReaderPtr<Reader>(GetPlatform().GetReader(kTransitColorFileName)).ReadAsString(data);
}
catch (RootException const & ex)
{
LOG(LWARNING, ("Loading transit colors failed:", ex.what()));
return;
}
try
{
base::Json root(data);
if (root.get() == nullptr)
return;
auto colors = json_object_get(root.get(), "colors");
if (colors == nullptr)
return;
char const * name = nullptr;
json_t * colorInfo = nullptr;
json_object_foreach(colors, name, colorInfo)
{
ASSERT(name != nullptr, ());
ASSERT(colorInfo != nullptr, ());
std::string strValue;
FromJSONObject(colorInfo, "clear", strValue);
m_clearColors[df::GetTransitColorName(name)] = ParseColor(strValue);
FromJSONObject(colorInfo, "night", strValue);
m_nightColors[df::GetTransitColorName(name)] = ParseColor(strValue);
FromJSONObject(colorInfo, "text", strValue);
m_clearColors[df::GetTransitTextColorName(name)] = ParseColor(strValue);
m_nightColors[df::GetTransitTextColorName(name)] = ParseColor(strValue);
}
}
catch (base::Json::Exception const & e)
{
LOG(LWARNING, ("Reading transit colors failed:", e.Msg()));
}
}
std::map<std::string, dp::Color> const & GetClearColors() const { return m_clearColors; }
private:
dp::Color ParseColor(std::string const & colorStr)
{
unsigned int color;
if (strings::to_uint(colorStr, color, 16))
return df::ToDrapeColor(static_cast<uint32_t>(color));
LOG(LWARNING, ("Color parsing failed:", colorStr));
return dp::Color();
}
std::map<std::string, dp::Color> m_clearColors;
std::map<std::string, dp::Color> m_nightColors;
};
TransitColorsHolder & TransitColors()
{
static TransitColorsHolder h;
return h;
}
} // namespace
namespace df
{
ColorConstant GetTransitColorName(ColorConstant const & localName)
{
return kTransitColorPrefix + kTransitLinePrefix + localName;
}
ColorConstant GetTransitTextColorName(ColorConstant const & localName)
{
return kTransitColorPrefix + kTransitTextPrefix + localName;
}
bool IsTransitColor(ColorConstant const & constant)
{
return constant.starts_with(kTransitColorPrefix);
}
dp::Color GetColorConstant(ColorConstant const & constant)
{
if (IsTransitColor(constant))
return TransitColors().GetColor(constant);
uint32_t const color = drule::rules().GetColor(constant);
return ToDrapeColor(color);
}
std::map<std::string, dp::Color> const & GetTransitClearColors()
{
return TransitColors().GetClearColors();
}
void LoadTransitColors()
{
TransitColors().Load();
}
} // namespace df

View file

@ -0,0 +1,22 @@
#pragma once
#include "drape/color.hpp"
#include <map>
#include <string>
namespace df
{
using ColorConstant = std::string;
inline std::string const kTransitColorPrefix = "transit_";
inline std::string const kTransitTextPrefix = "text_";
inline std::string const kTransitLinePrefix = "line_";
dp::Color GetColorConstant(ColorConstant const & constant);
std::map<std::string, dp::Color> const & GetTransitClearColors();
void LoadTransitColors();
ColorConstant GetTransitColorName(ColorConstant const & localName);
ColorConstant GetTransitTextColorName(ColorConstant const & localName);
} // namespace df

View file

@ -0,0 +1,314 @@
#include "drape_frontend/colored_symbol_shape.hpp"
#include "drape_frontend/render_state_extension.hpp"
#include "drape_frontend/visual_params.hpp"
#include "shaders/programs.hpp"
#include "drape/attribute_provider.hpp"
#include "drape/batcher.hpp"
#include "drape/glsl_func.hpp"
#include "drape/glsl_types.hpp"
#include "drape/overlay_handle.hpp"
#include "drape/texture_manager.hpp"
#include "drape/utils/vertex_decl.hpp"
namespace df
{
namespace
{
glsl::vec2 ShiftNormal(glsl::vec2 const & n, ColoredSymbolViewParams const & params)
{
glsl::vec2 result = n + glsl::vec2(params.m_offset.x, params.m_offset.y);
m2::PointF halfPixelSize;
if (params.m_shape == ColoredSymbolViewParams::Shape::Circle)
halfPixelSize = m2::PointF(params.m_radiusInPixels, params.m_radiusInPixels);
else
halfPixelSize = m2::PointF(0.5f * params.m_sizeInPixels.x, 0.5f * params.m_sizeInPixels.y);
if (params.m_anchor & dp::Top)
result.y += halfPixelSize.y;
else if (params.m_anchor & dp::Bottom)
result.y -= halfPixelSize.y;
if (params.m_anchor & dp::Left)
result.x += halfPixelSize.x;
else if (params.m_anchor & dp::Right)
result.x -= halfPixelSize.x;
return result;
}
} // namespace
class DynamicSquareHandle : public dp::SquareHandle
{
using TBase = dp::SquareHandle;
public:
DynamicSquareHandle(dp::OverlayID const & id, dp::Anchor anchor, m2::PointD const & gbPivot,
std::vector<m2::PointF> const & pxSizes, m2::PointD const & pxOffset, uint64_t priority,
bool isBound, int minVisibleScale, bool isBillboard)
: TBase(id, anchor, gbPivot, m2::PointD::Zero(), pxOffset, priority, isBound, minVisibleScale, isBillboard)
, m_pxSizes(pxSizes)
{
ASSERT_GREATER(pxSizes.size(), 0, ());
}
bool Update(ScreenBase const & screen) override
{
double zoom = 0.0;
int index = 0;
float lerpCoef = 0.0f;
ExtractZoomFactors(screen, zoom, index, lerpCoef);
auto const size = InterpolateByZoomLevels(index, lerpCoef, m_pxSizes);
m_pxHalfSize.x = size.x * 0.5;
m_pxHalfSize.y = size.y * 0.5;
return true;
}
private:
std::vector<m2::PointF> m_pxSizes;
};
ColoredSymbolShape::ColoredSymbolShape(m2::PointD const & mercatorPt, ColoredSymbolViewParams const & params,
TileKey const & tileKey, uint32_t textIndex, bool needOverlay)
: m_point(mercatorPt)
, m_params(params)
, m_tileCoords(tileKey.GetTileCoords())
, m_textIndex(textIndex)
, m_needOverlay(needOverlay)
{}
ColoredSymbolShape::ColoredSymbolShape(m2::PointD const & mercatorPt, ColoredSymbolViewParams const & params,
TileKey const & tileKey, uint32_t textIndex,
std::vector<m2::PointF> const & overlaySizes)
: m_point(mercatorPt)
, m_params(params)
, m_tileCoords(tileKey.GetTileCoords())
, m_textIndex(textIndex)
, m_needOverlay(true)
, m_overlaySizes(overlaySizes)
{}
void ColoredSymbolShape::Draw(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::Batcher> batcher,
ref_ptr<dp::TextureManager> textures) const
{
dp::TextureManager::ColorRegion colorRegion;
textures->GetColorRegion(m_params.m_color, colorRegion);
m2::PointF const & colorUv = colorRegion.GetTexRect().Center();
dp::TextureManager::ColorRegion outlineColorRegion;
textures->GetColorRegion(m_params.m_outlineColor, outlineColorRegion);
m2::PointF const & outlineUv = outlineColorRegion.GetTexRect().Center();
using V = gpu::ColoredSymbolVertex;
V::TTexCoord const uv(colorUv.x, colorUv.y, 0.0f, 0.0f);
V::TTexCoord const uvOutline(outlineUv.x, outlineUv.y, 0.0f, 0.0f);
glsl::vec2 const pt = glsl::ToVec2(ConvertToLocal(m_point, m_params.m_tileCenter, kShapeCoordScalar));
glsl::vec3 const position = glsl::vec3(pt, m_params.m_depth);
buffer_vector<V, 48> buffer;
auto norm = [this](float x, float y) { return ShiftNormal(glsl::vec2(x, y), m_params); };
m2::PointU pixelSize;
if (m_params.m_shape == ColoredSymbolViewParams::Shape::Circle)
{
pixelSize = m2::PointU(2 * static_cast<uint32_t>(m_params.m_radiusInPixels),
2 * static_cast<uint32_t>(m_params.m_radiusInPixels));
// Here we use an equilateral triangle to render circle (incircle of a triangle).
static float constexpr kSqrt3 = 1.732050808f;
float r = m_params.m_radiusInPixels - m_params.m_outlineWidth;
V::TTexCoord uv2(uv.x, uv.y, norm(0.0, 0.0));
buffer.push_back(V(position, V::TNormal(-r * kSqrt3, -r, r, 1.0f), uv2));
buffer.push_back(V(position, V::TNormal(r * kSqrt3, -r, r, 1.0f), uv2));
buffer.push_back(V(position, V::TNormal(0.0f, 2.0f * r, r, 1.0f), uv2));
if (m_params.m_outlineWidth >= 1e-5)
{
r = m_params.m_radiusInPixels;
V::TTexCoord uvOutline2(uvOutline.x, uvOutline.y, norm(0.0, 0.0));
buffer.push_back(V(position, V::TNormal(-r * kSqrt3, -r, r, 1.0f), uvOutline2));
buffer.push_back(V(position, V::TNormal(r * kSqrt3, -r, r, 1.0f), uvOutline2));
buffer.push_back(V(position, V::TNormal(0.0f, 2.0f * r, r, 1.0f), uvOutline2));
}
}
else if (m_params.m_shape == ColoredSymbolViewParams::Shape::Rectangle)
{
pixelSize =
m2::PointU(static_cast<uint32_t>(m_params.m_sizeInPixels.x), static_cast<uint32_t>(m_params.m_sizeInPixels.y));
float const halfWidth = 0.5f * m_params.m_sizeInPixels.x;
float const halfHeight = 0.5f * m_params.m_sizeInPixels.y;
float const v = halfWidth * halfWidth + halfHeight * halfHeight;
float const halfWidthInside = halfWidth - m_params.m_outlineWidth;
float const halfHeightInside = halfHeight - m_params.m_outlineWidth;
buffer.push_back(V(position, V::TNormal(norm(-halfWidthInside, halfHeightInside), v, 0.0f), uv));
buffer.push_back(V(position, V::TNormal(norm(halfWidthInside, -halfHeightInside), v, 0.0f), uv));
buffer.push_back(V(position, V::TNormal(norm(halfWidthInside, halfHeightInside), v, 0.0f), uv));
buffer.push_back(V(position, V::TNormal(norm(-halfWidthInside, halfHeightInside), v, 0.0f), uv));
buffer.push_back(V(position, V::TNormal(norm(-halfWidthInside, -halfHeightInside), v, 0.0f), uv));
buffer.push_back(V(position, V::TNormal(norm(halfWidthInside, -halfHeightInside), v, 0.0f), uv));
if (m_params.m_outlineWidth >= 1e-5)
{
buffer.push_back(V(position, V::TNormal(norm(-halfWidth, halfHeight), v, 0.0f), uvOutline));
buffer.push_back(V(position, V::TNormal(norm(halfWidth, -halfHeight), v, 0.0f), uvOutline));
buffer.push_back(V(position, V::TNormal(norm(halfWidth, halfHeight), v, 0.0f), uvOutline));
buffer.push_back(V(position, V::TNormal(norm(-halfWidth, halfHeight), v, 0.0f), uvOutline));
buffer.push_back(V(position, V::TNormal(norm(-halfWidth, -halfHeight), v, 0.0f), uvOutline));
buffer.push_back(V(position, V::TNormal(norm(halfWidth, -halfHeight), v, 0.0f), uvOutline));
}
}
else if (m_params.m_shape == ColoredSymbolViewParams::Shape::RoundedRectangle)
{
pixelSize =
m2::PointU(static_cast<uint32_t>(m_params.m_sizeInPixels.x), static_cast<uint32_t>(m_params.m_sizeInPixels.y));
float const halfWidth = 0.5f * m_params.m_sizeInPixels.x;
float const halfHeight = 0.5f * m_params.m_sizeInPixels.y;
float const halfWidthBody = halfWidth - m_params.m_radiusInPixels;
float const halfHeightBody = halfHeight - m_params.m_radiusInPixels;
float const v = halfWidth * halfWidth + halfHeight * halfHeight;
float const halfWidthInside = halfWidth - m_params.m_outlineWidth;
float const halfHeightInside = halfHeight - m_params.m_outlineWidth;
if (halfWidthBody > 0.0f && halfHeightInside > 0.0f)
{
buffer.push_back(V(position, V::TNormal(norm(-halfWidthBody, halfHeightInside), v, 0.0f), uv));
buffer.push_back(V(position, V::TNormal(norm(halfWidthBody, -halfHeightInside), v, 0.0f), uv));
buffer.push_back(V(position, V::TNormal(norm(halfWidthBody, halfHeightInside), v, 0.0f), uv));
buffer.push_back(V(position, V::TNormal(norm(-halfWidthBody, halfHeightInside), v, 0.0f), uv));
buffer.push_back(V(position, V::TNormal(norm(-halfWidthBody, -halfHeightInside), v, 0.0f), uv));
buffer.push_back(V(position, V::TNormal(norm(halfWidthBody, -halfHeightInside), v, 0.0f), uv));
}
if (halfWidthInside > 0.0f && halfHeightBody > 0.0f)
{
buffer.push_back(V(position, V::TNormal(norm(-halfWidthInside, halfHeightBody), v, 0.0f), uv));
buffer.push_back(V(position, V::TNormal(norm(halfWidthInside, -halfHeightBody), v, 0.0f), uv));
buffer.push_back(V(position, V::TNormal(norm(halfWidthInside, halfHeightBody), v, 0.0f), uv));
buffer.push_back(V(position, V::TNormal(norm(-halfWidthInside, halfHeightBody), v, 0.0f), uv));
buffer.push_back(V(position, V::TNormal(norm(-halfWidthInside, -halfHeightBody), v, 0.0f), uv));
buffer.push_back(V(position, V::TNormal(norm(halfWidthInside, -halfHeightBody), v, 0.0f), uv));
}
// Here we use an right triangle to render a quarter of circle.
static float constexpr kSqrt2 = 1.414213562f;
float r = m_params.m_radiusInPixels - m_params.m_outlineWidth;
V::TTexCoord uv2(uv.x, uv.y, norm(-halfWidthBody, halfHeightBody));
buffer.push_back(V(position, V::TNormal(0.0, 0.0, r, 0.0f), uv2));
buffer.push_back(V(position, V::TNormal(0.0, r * kSqrt2, r, 0.0f), uv2));
buffer.push_back(V(position, V::TNormal(-r * kSqrt2, 0.0, r, 0.0f), uv2));
uv2 = V::TTexCoord(uv.x, uv.y, norm(halfWidthBody, halfHeightBody));
buffer.push_back(V(position, V::TNormal(0.0, 0.0, r, 0.0f), uv2));
buffer.push_back(V(position, V::TNormal(r * kSqrt2, 0.0, r, 0.0f), uv2));
buffer.push_back(V(position, V::TNormal(0.0, r * kSqrt2, r, 0.0f), uv2));
uv2 = V::TTexCoord(uv.x, uv.y, norm(halfWidthBody, -halfHeightBody));
buffer.push_back(V(position, V::TNormal(0.0, 0.0, r, 0.0f), uv2));
buffer.push_back(V(position, V::TNormal(0.0, -r * kSqrt2, r, 0.0f), uv2));
buffer.push_back(V(position, V::TNormal(r * kSqrt2, 0.0, r, 0.0f), uv2));
uv2 = V::TTexCoord(uv.x, uv.y, norm(-halfWidthBody, -halfHeightBody));
buffer.push_back(V(position, V::TNormal(0.0, 0.0, r, 0.0f), uv2));
buffer.push_back(V(position, V::TNormal(-r * kSqrt2, 0.0, r, 0.0f), uv2));
buffer.push_back(V(position, V::TNormal(0.0, -r * kSqrt2, r, 0.0f), uv2));
if (m_params.m_outlineWidth >= 1e-5)
{
if (halfWidthBody > 0.0f && halfHeight > 0.0f)
{
buffer.push_back(V(position, V::TNormal(norm(-halfWidthBody, halfHeight), v, 0.0f), uvOutline));
buffer.push_back(V(position, V::TNormal(norm(halfWidthBody, -halfHeight), v, 0.0f), uvOutline));
buffer.push_back(V(position, V::TNormal(norm(halfWidthBody, halfHeight), v, 0.0f), uvOutline));
buffer.push_back(V(position, V::TNormal(norm(-halfWidthBody, halfHeight), v, 0.0f), uvOutline));
buffer.push_back(V(position, V::TNormal(norm(-halfWidthBody, -halfHeight), v, 0.0f), uvOutline));
buffer.push_back(V(position, V::TNormal(norm(halfWidthBody, -halfHeight), v, 0.0f), uvOutline));
}
if (halfWidth > 0.0f && halfHeightBody > 0.0f)
{
buffer.push_back(V(position, V::TNormal(norm(-halfWidth, halfHeightBody), v, 0.0f), uvOutline));
buffer.push_back(V(position, V::TNormal(norm(halfWidth, -halfHeightBody), v, 0.0f), uvOutline));
buffer.push_back(V(position, V::TNormal(norm(halfWidth, halfHeightBody), v, 0.0f), uvOutline));
buffer.push_back(V(position, V::TNormal(norm(-halfWidth, halfHeightBody), v, 0.0f), uvOutline));
buffer.push_back(V(position, V::TNormal(norm(-halfWidth, -halfHeightBody), v, 0.0f), uvOutline));
buffer.push_back(V(position, V::TNormal(norm(halfWidth, -halfHeightBody), v, 0.0f), uvOutline));
}
r = m_params.m_radiusInPixels;
V::TTexCoord const uvOutline2(outlineUv.x, outlineUv.y, norm(-halfWidthBody, halfHeightBody));
buffer.push_back(V(position, V::TNormal(0.0, 0.0, r, 0.0f), uvOutline2));
buffer.push_back(V(position, V::TNormal(0.0, r * kSqrt2, r, 0.0f), uvOutline2));
buffer.push_back(V(position, V::TNormal(-r * kSqrt2, 0.0, r, 0.0f), uvOutline2));
uv2 = V::TTexCoord(outlineUv.x, outlineUv.y, norm(halfWidthBody, halfHeightBody));
buffer.push_back(V(position, V::TNormal(0.0, 0.0, r, 0.0f), uv2));
buffer.push_back(V(position, V::TNormal(r * kSqrt2, 0.0, r, 0.0f), uv2));
buffer.push_back(V(position, V::TNormal(0.0, r * kSqrt2, r, 0.0f), uv2));
uv2 = V::TTexCoord(outlineUv.x, outlineUv.y, norm(halfWidthBody, -halfHeightBody));
buffer.push_back(V(position, V::TNormal(0.0, 0.0, r, 0.0f), uv2));
buffer.push_back(V(position, V::TNormal(0.0, -r * kSqrt2, r, 0.0f), uv2));
buffer.push_back(V(position, V::TNormal(r * kSqrt2, 0.0, r, 0.0f), uv2));
uv2 = V::TTexCoord(outlineUv.x, outlineUv.y, norm(-halfWidthBody, -halfHeightBody));
buffer.push_back(V(position, V::TNormal(0.0, 0.0, r, 0.0f), uv2));
buffer.push_back(V(position, V::TNormal(-r * kSqrt2, 0.0, r, 0.0f), uv2));
buffer.push_back(V(position, V::TNormal(0.0, -r * kSqrt2, r, 0.0f), uv2));
}
}
if (buffer.empty())
return;
dp::OverlayID overlayId(m_params.m_featureId, m_params.m_markId, m_tileCoords, m_textIndex);
drape_ptr<dp::OverlayHandle> handle;
if (m_needOverlay)
{
if (!m_overlaySizes.empty())
{
handle = make_unique_dp<DynamicSquareHandle>(
overlayId, m_params.m_anchor, m_point, m_overlaySizes, m2::PointD(m_params.m_offset), GetOverlayPriority(),
true /* isBound */, m_params.m_minVisibleScale, true /* isBillboard */);
}
else
{
handle = make_unique_dp<dp::SquareHandle>(overlayId, m_params.m_anchor, m_point, m2::PointD(pixelSize),
m2::PointD(m_params.m_offset), GetOverlayPriority(), true /* isBound */,
m_params.m_minVisibleScale, true /* isBillboard */);
}
if (m_params.m_specialDisplacement == SpecialDisplacement::UserMark ||
m_params.m_specialDisplacement == SpecialDisplacement::SpecialModeUserMark)
{
handle->SetSpecialLayerOverlay(true);
}
handle->SetOverlayRank(m_params.m_startOverlayRank);
}
auto state = CreateRenderState(gpu::Program::ColoredSymbol, m_params.m_depthLayer);
state.SetProgram3d(gpu::Program::ColoredSymbolBillboard);
state.SetDepthTestEnabled(m_params.m_depthTestEnabled);
state.SetColorTexture(colorRegion.GetTexture());
state.SetDepthFunction(dp::TestFunction::Less);
dp::AttributeProvider provider(1, static_cast<uint32_t>(buffer.size()));
provider.InitStream(0, gpu::ColoredSymbolVertex::GetBindingInfo(), make_ref(buffer.data()));
batcher->InsertTriangleList(context, state, make_ref(&provider), std::move(handle));
}
uint64_t ColoredSymbolShape::GetOverlayPriority() const
{
if (m_params.m_specialDisplacement == SpecialDisplacement::SpecialModeUserMark)
return dp::CalculateSpecialModeUserMarkPriority(m_params.m_specialPriority);
if (m_params.m_specialDisplacement == SpecialDisplacement::UserMark)
return dp::CalculateUserMarkPriority(m_params.m_minVisibleScale, m_params.m_specialPriority);
return dp::CalculateOverlayPriority(m_params.m_rank, m_params.m_depth);
}
} // namespace df

View file

@ -0,0 +1,33 @@
#pragma once
#include "drape_frontend/map_shape.hpp"
#include "drape_frontend/shape_view_params.hpp"
#include "drape/constants.hpp"
namespace df
{
class ColoredSymbolShape : public MapShape
{
public:
ColoredSymbolShape(m2::PointD const & mercatorPt, ColoredSymbolViewParams const & params, TileKey const & tileKey,
uint32_t textIndex, bool needOverlay = true);
ColoredSymbolShape(m2::PointD const & mercatorPt, ColoredSymbolViewParams const & params, TileKey const & tileKey,
uint32_t textIndex, std::vector<m2::PointF> const & overlaySizes);
void Draw(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::Batcher> batcher,
ref_ptr<dp::TextureManager> textures) const override;
MapShapeType GetType() const override { return MapShapeType::OverlayType; }
private:
uint64_t GetOverlayPriority() const;
m2::PointD const m_point;
ColoredSymbolViewParams m_params;
m2::PointI const m_tileCoords;
uint32_t const m_textIndex;
bool const m_needOverlay;
std::vector<m2::PointF> m_overlaySizes;
};
} // namespace df

View file

@ -0,0 +1,30 @@
#pragma once
#include "indexer/feature_decl.hpp"
#include <map>
#include <memory>
#include <utility>
namespace df
{
using CustomFeatures = std::map<FeatureID, bool>;
struct CustomFeaturesContext
{
CustomFeatures const m_features;
explicit CustomFeaturesContext(CustomFeatures && features) : m_features(std::move(features)) {}
bool NeedDiscardGeometry(FeatureID const & id) const
{
auto const it = m_features.find(id);
if (it == m_features.cend())
return false;
return it->second;
}
};
using CustomFeaturesContextPtr = std::shared_ptr<CustomFeaturesContext>;
using CustomFeaturesContextWeakPtr = std::weak_ptr<CustomFeaturesContext>;
} // namespace df

View file

@ -0,0 +1,130 @@
#include "drape_frontend/debug_rect_renderer.hpp"
#include "drape_frontend/render_state_extension.hpp"
#include <vector>
namespace df
{
namespace
{
void PixelPointToScreenSpace(ScreenBase const & screen, m2::PointF const & pt, std::vector<float> & buffer)
{
auto const szX = static_cast<float>(screen.PixelRectIn3d().SizeX());
auto const szY = static_cast<float>(screen.PixelRectIn3d().SizeY());
buffer.push_back(2.0f * (pt.x / szX - 0.5f));
buffer.push_back(2.0f * (-pt.y / szY + 0.5f));
}
drape_ptr<dp::MeshObject> CreateMesh(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::GpuProgram> program,
std::vector<float> && vertices, std::string const & debugName)
{
auto mesh = make_unique_dp<dp::MeshObject>(context, dp::MeshObject::DrawPrimitive::LineStrip, debugName);
mesh->SetBuffer(0 /* bufferInd */, std::move(vertices), static_cast<uint32_t>(sizeof(float) * 2));
mesh->SetAttribute("a_position", 0 /* bufferInd */, 0 /* offset */, 2 /* componentsCount */);
mesh->Build(context, program);
CHECK(mesh->IsInitialized(), ());
return mesh;
}
} // namespace
DebugRectRenderer::DebugRectRenderer(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::GpuProgram> program,
ref_ptr<gpu::ProgramParamsSetter> paramsSetter)
: m_program(program)
, m_paramsSetter(paramsSetter)
, m_state(CreateRenderState(gpu::Program::DebugRect, DepthLayer::OverlayLayer))
{
m_state.SetDepthTestEnabled(false);
m_state.SetDrawAsLine(true);
m_state.SetLineWidth(1);
}
bool DebugRectRenderer::IsEnabled() const
{
return m_isEnabled;
}
void DebugRectRenderer::SetEnabled(bool enabled)
{
m_isEnabled = enabled;
if (!m_isEnabled)
{
m_rectMeshes.clear();
m_arrowMeshes.clear();
m_currentRectMesh = 0;
m_currentArrowMesh = 0;
}
}
void DebugRectRenderer::SetArrow(ref_ptr<dp::GraphicsContext> context, m2::PointF const & arrowStart,
m2::PointF const & arrowEnd, ScreenBase const & screen)
{
std::vector<float> vertices;
m2::PointF const dir = (arrowEnd - arrowStart).Normalize();
m2::PointF const side = m2::PointF(-dir.y, dir.x);
PixelPointToScreenSpace(screen, arrowStart, vertices);
PixelPointToScreenSpace(screen, arrowEnd, vertices);
PixelPointToScreenSpace(screen, arrowEnd - dir * 12 + side * 3, vertices);
PixelPointToScreenSpace(screen, arrowEnd, vertices);
PixelPointToScreenSpace(screen, arrowEnd - dir * 12 - side * 3, vertices);
ASSERT_LESS_OR_EQUAL(m_currentArrowMesh, m_arrowMeshes.size(), ());
if (m_currentArrowMesh == m_arrowMeshes.size())
m_arrowMeshes.emplace_back(CreateMesh(context, m_program, std::move(vertices), "DebugArrow"));
else
m_arrowMeshes[m_currentArrowMesh]->UpdateBuffer(context, 0 /* bufferInd */, vertices);
}
void DebugRectRenderer::SetRect(ref_ptr<dp::GraphicsContext> context, m2::RectF const & rect, ScreenBase const & screen)
{
std::vector<float> vertices;
PixelPointToScreenSpace(screen, rect.LeftBottom(), vertices);
PixelPointToScreenSpace(screen, rect.LeftTop(), vertices);
PixelPointToScreenSpace(screen, rect.RightTop(), vertices);
PixelPointToScreenSpace(screen, rect.RightBottom(), vertices);
PixelPointToScreenSpace(screen, rect.LeftBottom(), vertices);
ASSERT_LESS_OR_EQUAL(m_currentRectMesh, m_rectMeshes.size(), ());
if (m_currentRectMesh == m_rectMeshes.size())
m_rectMeshes.emplace_back(CreateMesh(context, m_program, std::move(vertices), "DebugRect"));
else
m_rectMeshes[m_currentRectMesh]->UpdateBuffer(context, 0 /* bufferInd */, vertices);
}
void DebugRectRenderer::DrawRect(ref_ptr<dp::GraphicsContext> context, ScreenBase const & screen,
m2::RectF const & rect, dp::Color const & color)
{
if (!m_isEnabled)
return;
SetRect(context, rect, screen);
gpu::DebugRectProgramParams params;
params.m_color = glsl::ToVec4(color);
m_rectMeshes[m_currentRectMesh++]->Render(context, m_program, m_state, m_paramsSetter, params);
}
void DebugRectRenderer::DrawArrow(ref_ptr<dp::GraphicsContext> context, ScreenBase const & screen,
dp::OverlayTree::DisplacementData const & data)
{
if (!m_isEnabled)
return;
if (data.m_arrowStart.EqualDxDy(data.m_arrowEnd, 1e-5f))
return;
SetArrow(context, data.m_arrowStart, data.m_arrowEnd, screen);
gpu::DebugRectProgramParams params;
params.m_color = glsl::ToVec4(data.m_arrowColor);
m_arrowMeshes[m_currentArrowMesh++]->Render(context, m_program, m_state, m_paramsSetter, params);
}
void DebugRectRenderer::FinishRendering()
{
m_currentRectMesh = 0;
m_currentArrowMesh = 0;
}
} // namespace df

View file

@ -0,0 +1,50 @@
#pragma once
#include "drape/debug_renderer.hpp"
#include "drape/gpu_program.hpp"
#include "drape/mesh_object.hpp"
#include "drape/render_state.hpp"
#include "shaders/program_params.hpp"
#include "geometry/rect2d.hpp"
#include "geometry/screenbase.hpp"
#include <vector>
namespace df
{
class DebugRectRenderer : public dp::DebugRenderer
{
using Base = dp::MeshObject;
public:
DebugRectRenderer(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::GpuProgram> program,
ref_ptr<gpu::ProgramParamsSetter> paramsSetter);
void SetEnabled(bool enabled);
bool IsEnabled() const override;
void DrawRect(ref_ptr<dp::GraphicsContext> context, ScreenBase const & screen, m2::RectF const & rect,
dp::Color const & color) override;
void DrawArrow(ref_ptr<dp::GraphicsContext> context, ScreenBase const & screen,
dp::OverlayTree::DisplacementData const & data) override;
void FinishRendering();
private:
void SetArrow(ref_ptr<dp::GraphicsContext> context, m2::PointF const & arrowStart, m2::PointF const & arrowEnd,
ScreenBase const & screen);
void SetRect(ref_ptr<dp::GraphicsContext> context, m2::RectF const & rect, ScreenBase const & screen);
std::vector<drape_ptr<dp::MeshObject>> m_rectMeshes;
std::vector<drape_ptr<dp::MeshObject>> m_arrowMeshes;
size_t m_currentRectMesh = 0;
size_t m_currentArrowMesh = 0;
ref_ptr<dp::GpuProgram> m_program;
ref_ptr<gpu::ProgramParamsSetter> m_paramsSetter;
dp::RenderState m_state;
bool m_isEnabled = false;
};
} // namespace df

View file

@ -0,0 +1,73 @@
#include "drape_frontend/drape_api.hpp"
#include "drape_frontend/drape_engine.hpp"
#include "drape_frontend/message_subclasses.hpp"
namespace df
{
void DrapeApi::SetDrapeEngine(ref_ptr<DrapeEngine> engine)
{
m_engine.Set(engine);
}
void DrapeApi::AddLine(std::string const & id, DrapeApiLineData const & data)
{
DrapeEngineLockGuard lock(m_engine);
if (!lock)
return;
auto & threadCommutator = lock.Get()->m_threadCommutator;
auto const it = m_lines.find(id);
if (it != m_lines.end())
{
threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread, make_unique_dp<DrapeApiRemoveMessage>(id),
MessagePriority::Normal);
}
m_lines[id] = data;
TLines lines;
lines.insert(std::make_pair(id, data));
threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread, make_unique_dp<DrapeApiAddLinesMessage>(lines),
MessagePriority::Normal);
}
void DrapeApi::RemoveLine(std::string const & id)
{
DrapeEngineLockGuard lock(m_engine);
if (!lock)
return;
auto & threadCommutator = lock.Get()->m_threadCommutator;
m_lines.erase(id);
threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread, make_unique_dp<DrapeApiRemoveMessage>(id),
MessagePriority::Normal);
}
void DrapeApi::Clear()
{
DrapeEngineLockGuard lock(m_engine);
if (!lock)
return;
auto & threadCommutator = lock.Get()->m_threadCommutator;
m_lines.clear();
threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
make_unique_dp<DrapeApiRemoveMessage>("", true /* remove all */),
MessagePriority::Normal);
}
void DrapeApi::Invalidate()
{
DrapeEngineLockGuard lock(m_engine);
if (!lock)
return;
auto & threadCommutator = lock.Get()->m_threadCommutator;
threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
make_unique_dp<DrapeApiRemoveMessage>("", true /* remove all */),
MessagePriority::Normal);
threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
make_unique_dp<DrapeApiAddLinesMessage>(m_lines), MessagePriority::Normal);
}
} // namespace df

View file

@ -0,0 +1,70 @@
#pragma once
#include "drape_frontend/drape_engine_safe_ptr.hpp"
#include "drape/color.hpp"
#include "drape/pointers.hpp"
#include "geometry/point2d.hpp"
#include <mutex>
#include <string>
#include <unordered_map>
#include <vector>
namespace df
{
struct DrapeApiLineData
{
DrapeApiLineData() = default;
DrapeApiLineData(std::vector<m2::PointD> const & points, dp::Color const & color) : m_points(points), m_color(color)
{}
DrapeApiLineData & ShowPoints(bool markPoints)
{
m_showPoints = true;
m_markPoints = markPoints;
return *this;
}
DrapeApiLineData & Width(float width)
{
m_width = width;
return *this;
}
DrapeApiLineData & ShowId()
{
m_showId = true;
return *this;
}
std::vector<m2::PointD> m_points;
float m_width = 1.0f;
dp::Color m_color;
bool m_showPoints = false;
bool m_markPoints = false;
bool m_showId = false;
};
class DrapeApi
{
public:
using TLines = std::unordered_map<std::string, DrapeApiLineData>;
DrapeApi() = default;
void SetDrapeEngine(ref_ptr<DrapeEngine> engine);
void AddLine(std::string const & id, DrapeApiLineData const & data);
void RemoveLine(std::string const & id);
void Clear();
void Invalidate();
private:
DrapeEngineSafePtr m_engine;
TLines m_lines;
};
} // namespace df

View file

@ -0,0 +1,125 @@
#include "drape_frontend/drape_api_builder.hpp"
#include "drape_frontend/batcher_bucket.hpp"
#include "drape_frontend/colored_symbol_shape.hpp"
#include "drape_frontend/gui/gui_text.hpp"
#include "drape_frontend/line_shape.hpp"
#include "drape_frontend/shape_view_params.hpp"
#include "drape/batcher.hpp"
#include "indexer/feature_decl.hpp"
#include "base/string_utils.hpp"
namespace
{
void BuildText(ref_ptr<dp::GraphicsContext> context, std::string const & str, dp::FontDecl const & font,
m2::PointD const & position, m2::PointD const & center, ref_ptr<dp::TextureManager> textures,
dp::Batcher & batcher)
{
gui::StaticLabel::LabelResult result;
gui::StaticLabel::CacheStaticText(str, "\n", dp::LeftTop, font, textures, result);
glsl::vec2 const pt = glsl::ToVec2(df::MapShape::ConvertToLocal(position, center, df::kShapeCoordScalar));
for (gui::StaticLabel::Vertex & v : result.m_buffer)
v.m_position = glsl::vec3(pt, 0.0f);
dp::AttributeProvider provider(1 /* streamCount */, static_cast<uint32_t>(result.m_buffer.size()));
provider.InitStream(0 /* streamIndex */, gui::StaticLabel::Vertex::GetBindingInfo(),
make_ref(result.m_buffer.data()));
batcher.InsertListOfStrip(context, result.m_state, make_ref(&provider), dp::Batcher::VertexPerQuad);
}
} // namespace
namespace df
{
void DrapeApiBuilder::BuildLines(ref_ptr<dp::GraphicsContext> context, DrapeApi::TLines & lines,
ref_ptr<dp::TextureManager> textures,
std::vector<drape_ptr<DrapeApiRenderProperty>> & properties)
{
properties.reserve(lines.size());
uint32_t constexpr kMaxSize = 5000;
uint32_t constexpr kFontSize = 14;
FeatureID fakeFeature;
for (auto & line : lines)
{
std::string const & id = line.first;
DrapeApiLineData & data = line.second;
base::Unique(data.m_points,
[](m2::PointD const & p1, m2::PointD const & p2) { return p1.EqualDxDy(p2, kMwmPointAccuracy); });
m2::RectD rect;
for (auto const & p : data.m_points)
rect.Add(p);
dp::Batcher batcher(kMaxSize, kMaxSize);
batcher.SetBatcherHash(static_cast<uint64_t>(BatcherBucket::Default));
auto property = make_unique_dp<DrapeApiRenderProperty>();
property->m_center = rect.Center();
{
dp::SessionGuard guard(context, batcher,
[&property, &id](dp::RenderState const & state, drape_ptr<dp::RenderBucket> && b)
{
property->m_id = id;
property->m_buckets.emplace_back(state, std::move(b));
});
if (data.m_points.size() > 1)
{
m2::SharedSpline spline(data.m_points);
LineViewParams lvp;
lvp.m_tileCenter = property->m_center;
lvp.m_depthTestEnabled = false;
lvp.m_minVisibleScale = 1;
lvp.m_cap = dp::RoundCap;
lvp.m_color = data.m_color;
lvp.m_width = data.m_width;
lvp.m_join = dp::RoundJoin;
LineShape(spline, lvp).Draw(context, make_ref(&batcher), textures);
}
if (data.m_showPoints)
{
ColoredSymbolViewParams cvp;
cvp.m_tileCenter = property->m_center;
cvp.m_depthTestEnabled = false;
cvp.m_minVisibleScale = 1;
cvp.m_shape = ColoredSymbolViewParams::Shape::Circle;
cvp.m_color = data.m_color;
cvp.m_radiusInPixels = data.m_width * 2.0f;
for (m2::PointD const & pt : data.m_points)
{
ColoredSymbolShape(m2::PointD(pt), cvp, TileKey(), 0 /* textIndex */, false /* need overlay */)
.Draw(context, make_ref(&batcher), textures);
}
}
if (data.m_markPoints || data.m_showId)
{
dp::FontDecl font(data.m_color, kFontSize);
size_t index = 0;
for (m2::PointD const & pt : data.m_points)
{
if (index > 0 && !data.m_markPoints)
break;
std::string s;
if (data.m_markPoints)
s = strings::to_string(index) + ((data.m_showId && index == 0) ? (" (" + id + ")") : "");
else
s = id;
BuildText(context, s, font, pt, property->m_center, textures, batcher);
index++;
}
}
}
if (!property->m_buckets.empty())
properties.push_back(std::move(property));
}
}
} // namespace df

View file

@ -0,0 +1,28 @@
#pragma once
#include "drape_frontend/drape_api.hpp"
#include "drape/render_bucket.hpp"
#include "drape/render_state.hpp"
#include "drape/texture_manager.hpp"
#include <string>
#include <utility>
#include <vector>
namespace df
{
struct DrapeApiRenderProperty
{
std::string m_id;
m2::PointD m_center;
std::vector<std::pair<dp::RenderState, drape_ptr<dp::RenderBucket>>> m_buckets;
};
class DrapeApiBuilder
{
public:
void BuildLines(ref_ptr<dp::GraphicsContext> context, DrapeApi::TLines & lines, ref_ptr<dp::TextureManager> textures,
std::vector<drape_ptr<DrapeApiRenderProperty>> & properties);
};
} // namespace df

View file

@ -0,0 +1,82 @@
#include "drape_frontend/drape_api_renderer.hpp"
#include "drape_frontend/shape_view_params.hpp"
#include "drape_frontend/visual_params.hpp"
#include "drape/overlay_handle.hpp"
#include "drape/vertex_array_buffer.hpp"
#include <algorithm>
namespace df
{
void DrapeApiRenderer::AddRenderProperties(ref_ptr<dp::GraphicsContext> context, ref_ptr<gpu::ProgramManager> mng,
std::vector<drape_ptr<DrapeApiRenderProperty>> && properties)
{
if (properties.empty())
return;
size_t const startIndex = m_properties.size();
m_properties.reserve(m_properties.size() + properties.size());
std::move(properties.begin(), properties.end(), std::back_inserter(m_properties));
for (size_t i = startIndex; i < m_properties.size(); i++)
{
for (auto const & bucket : m_properties[i]->m_buckets)
{
auto program = mng->GetProgram(bucket.first.GetProgram<gpu::Program>());
program->Bind();
bucket.second->GetBuffer()->Build(context, program);
}
}
}
void DrapeApiRenderer::RemoveRenderProperty(std::string const & id)
{
m_properties.erase(
std::remove_if(m_properties.begin(), m_properties.end(), [&id](auto const & p) { return p->m_id == id; }),
m_properties.end());
}
void DrapeApiRenderer::Clear()
{
m_properties.clear();
}
void DrapeApiRenderer::Render(ref_ptr<dp::GraphicsContext> context, ref_ptr<gpu::ProgramManager> mng,
ScreenBase const & screen, FrameValues const & frameValues)
{
if (m_properties.empty())
return;
auto const & glyphParams = df::VisualParams::Instance().GetGlyphVisualParams();
for (auto const & property : m_properties)
{
math::Matrix<float, 4, 4> const mv = screen.GetModelView(property->m_center, kShapeCoordScalar);
for (auto const & bucket : property->m_buckets)
{
auto program = mng->GetProgram(bucket.first.GetProgram<gpu::Program>());
program->Bind();
dp::ApplyState(context, program, bucket.first);
auto const p = bucket.first.GetProgram<gpu::Program>();
if (p == gpu::Program::TextOutlinedGui || p == gpu::Program::TextStaticOutlinedGui)
{
gpu::GuiProgramParams params;
frameValues.SetTo(params);
params.m_modelView = glsl::make_mat4(mv.m_data);
params.m_contrastGamma = glsl::vec2(glyphParams.m_guiContrast, glyphParams.m_guiGamma);
params.m_isOutlinePass = 0.0f;
mng->GetParamsSetter()->Apply(context, program, params);
}
else
{
gpu::MapProgramParams params;
frameValues.SetTo(params);
params.m_modelView = glsl::make_mat4(mv.m_data);
mng->GetParamsSetter()->Apply(context, program, params);
}
bucket.second->Render(context, bucket.first.GetDrawAsLine());
}
}
}
} // namespace df

View file

@ -0,0 +1,31 @@
#pragma once
#include "drape_frontend/drape_api_builder.hpp"
#include "drape_frontend/frame_values.hpp"
#include "shaders/program_manager.hpp"
#include "geometry/screenbase.hpp"
#include <string>
#include <vector>
namespace df
{
class DrapeApiRenderer
{
public:
DrapeApiRenderer() = default;
void AddRenderProperties(ref_ptr<dp::GraphicsContext> context, ref_ptr<gpu::ProgramManager> mng,
std::vector<drape_ptr<DrapeApiRenderProperty>> && properties);
void RemoveRenderProperty(std::string const & id);
void Clear();
void Render(ref_ptr<dp::GraphicsContext> context, ref_ptr<gpu::ProgramManager> mng, ScreenBase const & screen,
FrameValues const & frameValues);
private:
std::vector<drape_ptr<DrapeApiRenderProperty>> m_properties;
};
} // namespace df

View file

@ -0,0 +1,918 @@
#include "drape_frontend/drape_engine.hpp"
#include "drape_frontend/gui/drape_gui.hpp"
#include "drape_frontend/message_subclasses.hpp"
#include "drape_frontend/my_position_controller.hpp"
#include "drape_frontend/visual_params.hpp"
#include "drape/drape_routine.hpp"
#include "drape/support_manager.hpp"
#include "platform/settings.hpp"
#include <unordered_map>
namespace df
{
using namespace std::placeholders;
namespace
{
std::string_view constexpr kLocationStateMode = "LastLocationStateMode";
std::string_view constexpr kLastEnterBackground = "LastEnterBackground";
} // namespace
DrapeEngine::DrapeEngine(Params && params)
: m_myPositionModeChanged(std::move(params.m_myPositionModeChanged))
, m_viewport(std::move(params.m_viewport))
{
dp::DrapeRoutine::Init();
VisualParams::Init(params.m_vs, df::CalculateTileSize(m_viewport.GetWidth(), m_viewport.GetHeight()));
SetFontScaleFactor(params.m_fontsScaleFactor);
gui::DrapeGui::Instance().SetSurfaceSize(m2::PointF(m_viewport.GetWidth(), m_viewport.GetHeight()));
m_textureManager = make_unique_dp<dp::TextureManager>();
m_threadCommutator = make_unique_dp<ThreadsCommutator>();
m_requestedTiles = make_unique_dp<RequestedTiles>();
using namespace location;
EMyPositionMode mode = PendingPosition;
if (settings::Get(kLocationStateMode, mode) && mode == FollowAndRotate)
{
// If the screen rect setting in follow and rotate mode is missing or invalid, it could cause
// invalid animations, so the follow and rotate mode should be discarded.
m2::AnyRectD rect;
if (!(settings::Get("ScreenClipRect", rect) && df::GetWorldRect().IsRectInside(rect.GetGlobalRect())))
mode = Follow;
}
if (!settings::Get(kLastEnterBackground, m_startBackgroundTime))
m_startBackgroundTime = base::Timer::LocalTime();
std::vector<PostprocessRenderer::Effect> effects;
// bool enabledAntialiasing;
// if (!settings::Get(dp::kSupportedAntialiasing, enabledAntialiasing))
// enabledAntialiasing = false;
// Turn off AA for a while by energy-saving issues.
// if (enabledAntialiasing)
//{
// LOG(LINFO, ("Antialiasing is enabled"));
// effects.push_back(PostprocessRenderer::Antialiasing);
//}
MyPositionController::Params mpParams(mode, base::Timer::LocalTime() - m_startBackgroundTime, params.m_hints,
params.m_isRoutingActive, params.m_isAutozoomEnabled,
std::bind(&DrapeEngine::MyPositionModeChanged, this, _1, _2));
FrontendRenderer::Params frParams(
params.m_apiVersion, make_ref(m_threadCommutator), params.m_factory, make_ref(m_textureManager),
std::move(mpParams), m_viewport, std::bind(&DrapeEngine::ModelViewChanged, this, _1),
std::bind(&DrapeEngine::TapEvent, this, _1), std::bind(&DrapeEngine::UserPositionChanged, this, _1, _2),
make_ref(m_requestedTiles), std::move(params.m_overlaysShowStatsCallback), params.m_allow3dBuildings,
params.m_trafficEnabled, params.m_blockTapEvents, std::move(effects), params.m_onGraphicsContextInitialized,
std::move(params.m_renderInjectionHandler));
BackendRenderer::Params brParams(params.m_apiVersion, frParams.m_commutator, frParams.m_oglContextFactory,
frParams.m_texMng, params.m_model, params.m_model.UpdateCurrentCountryFn(),
make_ref(m_requestedTiles), params.m_allow3dBuildings, params.m_trafficEnabled,
params.m_isolinesEnabled, params.m_simplifiedTrafficColors,
std::move(params.m_arrow3dCustomDecl), params.m_onGraphicsContextInitialized);
m_backend = make_unique_dp<BackendRenderer>(std::move(brParams));
m_frontend = make_unique_dp<FrontendRenderer>(std::move(frParams));
m_widgetsInfo = std::move(params.m_info);
RecacheGui(false);
RecacheMapShapes();
if (params.m_showChoosePositionMark)
EnableChoosePositionMode(true, std::move(params.m_boundAreaTriangles), nullptr);
ResizeImpl(m_viewport.GetWidth(), m_viewport.GetHeight());
}
DrapeEngine::~DrapeEngine()
{
dp::DrapeRoutine::Shutdown();
// Call Teardown explicitly! We must wait for threads completion.
m_frontend->Teardown();
m_backend->Teardown();
// Reset thread commutator, it stores BaseRenderer pointers.
m_threadCommutator.reset();
// Reset pointers to FrontendRenderer and BackendRenderer.
m_frontend.reset();
m_backend.reset();
gui::DrapeGui::Instance().Destroy();
m_textureManager->Release();
}
void DrapeEngine::RecoverSurface(int w, int h, bool recreateContextDependentResources)
{
if (m_choosePositionMode)
{
m_threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
make_unique_dp<ShowChoosePositionMarkMessage>(), MessagePriority::Normal);
}
if (recreateContextDependentResources)
{
RecacheGui(false);
RecacheMapShapes();
m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<RecoverContextDependentResourcesMessage>(), MessagePriority::Normal);
}
ResizeImpl(w, h);
}
void DrapeEngine::Resize(int w, int h)
{
ASSERT_GREATER(w, 0, ());
ASSERT_GREATER(h, 0, ());
if (m_viewport.GetHeight() != static_cast<uint32_t>(h) || m_viewport.GetWidth() != static_cast<uint32_t>(w))
ResizeImpl(w, h);
}
void DrapeEngine::SetVisibleViewport(m2::RectD const & rect) const
{
m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread, make_unique_dp<SetVisibleViewportMessage>(rect),
MessagePriority::Normal);
}
void DrapeEngine::Invalidate()
{
m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread, make_unique_dp<InvalidateMessage>(),
MessagePriority::High);
}
void DrapeEngine::AddTouchEvent(TouchEvent const & event)
{
AddUserEvent(make_unique_dp<TouchEvent>(event));
}
void DrapeEngine::Scale(double factor, m2::PointD const & pxPoint, bool isAnim)
{
AddUserEvent(make_unique_dp<ScaleEvent>(factor, pxPoint, isAnim));
}
void DrapeEngine::Move(double factorX, double factorY, bool isAnim)
{
AddUserEvent(make_unique_dp<MoveEvent>(factorX, factorY, isAnim));
}
void DrapeEngine::Scroll(double distanceX, double distanceY)
{
AddUserEvent(make_unique_dp<ScrollEvent>(distanceX, distanceY));
}
void DrapeEngine::Rotate(double azimuth, bool isAnim)
{
AddUserEvent(make_unique_dp<RotateEvent>(azimuth, isAnim, nullptr /* parallelAnimCreator */));
}
void DrapeEngine::MakeFrameActive()
{
AddUserEvent(make_unique_dp<ActiveFrameEvent>());
}
void DrapeEngine::ScaleAndSetCenter(m2::PointD const & centerPt, double scaleFactor, bool isAnim,
bool trackVisibleViewport)
{
PostUserEvent(make_unique_dp<SetCenterEvent>(scaleFactor, centerPt, isAnim, trackVisibleViewport,
nullptr /* parallelAnimCreator */));
}
void DrapeEngine::SetModelViewCenter(m2::PointD const & centerPt, int zoom, bool isAnim, bool trackVisibleViewport)
{
PostUserEvent(
make_unique_dp<SetCenterEvent>(centerPt, zoom, isAnim, trackVisibleViewport, nullptr /* parallelAnimCreator */));
}
void DrapeEngine::SetModelViewRect(m2::RectD const & rect, bool applyRotation, int zoom, bool isAnim,
bool useVisibleViewport)
{
PostUserEvent(make_unique_dp<SetRectEvent>(rect, applyRotation, zoom, isAnim, useVisibleViewport,
nullptr /* parallelAnimCreator */));
}
void DrapeEngine::SetModelViewAnyRect(m2::AnyRectD const & rect, bool isAnim, bool useVisibleViewport)
{
PostUserEvent(make_unique_dp<SetAnyRectEvent>(rect, isAnim, true /* fitInViewport */, useVisibleViewport));
}
void DrapeEngine::ClearUserMarksGroup(kml::MarkGroupId groupId)
{
m_threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
make_unique_dp<ClearUserMarkGroupMessage>(groupId), MessagePriority::Normal);
}
void DrapeEngine::ChangeVisibilityUserMarksGroup(kml::MarkGroupId groupId, bool isVisible)
{
m_threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
make_unique_dp<ChangeUserMarkGroupVisibilityMessage>(groupId, isVisible),
MessagePriority::Normal);
}
void DrapeEngine::InvalidateUserMarks()
{
m_threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread, make_unique_dp<InvalidateUserMarksMessage>(),
MessagePriority::Normal);
}
void DrapeEngine::UpdateUserMarks(UserMarksProvider * provider, bool firstTime)
{
auto const updatedGroupIds = firstTime ? provider->GetAllGroupIds() : provider->GetUpdatedGroupIds();
if (updatedGroupIds.empty())
return;
auto marksRenderCollection = make_unique_dp<UserMarksRenderCollection>();
auto linesRenderCollection = make_unique_dp<UserLinesRenderCollection>();
auto justCreatedIdCollection = make_unique_dp<IDCollections>();
auto removedIdCollection = make_unique_dp<IDCollections>();
std::unordered_map<kml::MarkGroupId, drape_ptr<IDCollections>> groupsVisibleIds;
auto const groupFilter = [&](kml::MarkGroupId groupId)
{ return provider->IsGroupVisible(groupId) && (provider->GetBecameVisibleGroupIds().count(groupId) == 0); };
using GroupFilter = std::function<bool(kml::MarkGroupId groupId)>;
auto const collectIds = [&](kml::MarkIdSet const & markIds, kml::TrackIdSet const & lineIds,
GroupFilter const & filter, IDCollections & collection)
{
for (auto const markId : markIds)
if (filter == nullptr || filter(provider->GetUserPointMark(markId)->GetGroupId()))
collection.m_markIds.push_back(markId);
for (auto const lineId : lineIds)
if (filter == nullptr || filter(provider->GetUserLineMark(lineId)->GetGroupId()))
collection.m_lineIds.push_back(lineId);
};
auto const collectRenderData =
[&](kml::MarkIdSet const & markIds, kml::TrackIdSet const & lineIds, GroupFilter const & filter)
{
for (auto const markId : markIds)
{
auto const mark = provider->GetUserPointMark(markId);
if (filter == nullptr || filter(mark->GetGroupId()))
marksRenderCollection->emplace(markId, GenerateMarkRenderInfo(mark));
}
for (auto const lineId : lineIds)
{
auto const line = provider->GetUserLineMark(lineId);
if (filter == nullptr || filter(line->GetGroupId()))
linesRenderCollection->emplace(lineId, GenerateLineRenderInfo(line));
}
};
if (firstTime)
{
for (auto groupId : provider->GetAllGroupIds())
{
auto visibleIdCollection = make_unique_dp<IDCollections>();
if (provider->IsGroupVisible(groupId))
{
collectIds(provider->GetGroupPointIds(groupId), provider->GetGroupLineIds(groupId), nullptr /* filter */,
*visibleIdCollection);
collectRenderData(provider->GetGroupPointIds(groupId), provider->GetGroupLineIds(groupId),
nullptr /* filter */);
}
groupsVisibleIds.emplace(groupId, std::move(visibleIdCollection));
}
}
else
{
for (auto groupId : provider->GetUpdatedGroupIds())
{
auto visibleIdCollection = make_unique_dp<IDCollections>();
if (provider->IsGroupVisible(groupId))
{
collectIds(provider->GetGroupPointIds(groupId), provider->GetGroupLineIds(groupId), nullptr /* filter */,
*visibleIdCollection);
}
groupsVisibleIds.emplace(groupId, std::move(visibleIdCollection));
}
collectIds(provider->GetCreatedMarkIds(), provider->GetCreatedLineIds(), groupFilter, *justCreatedIdCollection);
collectIds(provider->GetRemovedMarkIds(), provider->GetRemovedLineIds(), nullptr /* filter */,
*removedIdCollection);
collectRenderData(provider->GetCreatedMarkIds(), provider->GetCreatedLineIds(), groupFilter);
collectRenderData(provider->GetUpdatedMarkIds(), provider->GetUpdatedLineIds(), groupFilter);
for (auto const groupId : provider->GetBecameVisibleGroupIds())
collectRenderData(provider->GetGroupPointIds(groupId), provider->GetGroupLineIds(groupId), nullptr /* filter */);
for (auto const groupId : provider->GetBecameInvisibleGroupIds())
{
collectIds(provider->GetGroupPointIds(groupId), provider->GetGroupLineIds(groupId), nullptr /* filter */,
*removedIdCollection);
}
}
if (!marksRenderCollection->empty() || !linesRenderCollection->empty() || !removedIdCollection->IsEmpty() ||
!justCreatedIdCollection->IsEmpty())
{
m_threadCommutator->PostMessage(
ThreadsCommutator::ResourceUploadThread,
make_unique_dp<UpdateUserMarksMessage>(std::move(justCreatedIdCollection), std::move(removedIdCollection),
std::move(marksRenderCollection), std::move(linesRenderCollection)),
MessagePriority::Normal);
}
for (auto & v : groupsVisibleIds)
{
m_threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
make_unique_dp<UpdateUserMarkGroupMessage>(v.first, std::move(v.second)),
MessagePriority::Normal);
}
}
void DrapeEngine::SetRenderingEnabled(ref_ptr<dp::GraphicsContextFactory> contextFactory)
{
m_backend->SetRenderingEnabled(contextFactory);
m_frontend->SetRenderingEnabled(contextFactory);
LOG(LDEBUG, ("Rendering enabled"));
}
void DrapeEngine::SetRenderingDisabled(bool const destroySurface)
{
m_frontend->SetRenderingDisabled(destroySurface);
m_backend->SetRenderingDisabled(destroySurface);
LOG(LDEBUG, ("Rendering disabled"));
}
void DrapeEngine::InvalidateRect(m2::RectD const & rect)
{
m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread, make_unique_dp<InvalidateRectMessage>(rect),
MessagePriority::High);
}
void DrapeEngine::UpdateMapStyle()
{
m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread, make_unique_dp<UpdateMapStyleMessage>(),
MessagePriority::High);
}
void DrapeEngine::RecacheMapShapes()
{
m_threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread, make_unique_dp<MapShapesRecacheMessage>(),
MessagePriority::Normal);
}
dp::DrapeID DrapeEngine::GenerateDrapeID()
{
return ++m_drapeIdGenerator;
}
void DrapeEngine::RecacheGui(bool needResetOldGui)
{
m_threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
make_unique_dp<GuiRecacheMessage>(m_widgetsInfo, needResetOldGui),
MessagePriority::Normal);
}
void DrapeEngine::PostUserEvent(drape_ptr<UserEvent> && e)
{
m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread, make_unique_dp<PostUserEventMessage>(std::move(e)),
MessagePriority::Normal);
}
void DrapeEngine::AddUserEvent(drape_ptr<UserEvent> && e)
{
m_frontend->AddUserEvent(std::move(e));
}
void DrapeEngine::ModelViewChanged(ScreenBase const & screen)
{
if (m_modelViewChangedHandler != nullptr)
m_modelViewChangedHandler(screen);
}
void DrapeEngine::MyPositionModeChanged(location::EMyPositionMode mode, bool routingActive)
{
settings::Set(kLocationStateMode, mode);
if (m_myPositionModeChanged)
m_myPositionModeChanged(mode, routingActive);
}
location::EMyPositionMode DrapeEngine::GetMyPositionMode() const
{
return m_frontend->GetMyPositionMode();
}
void DrapeEngine::TapEvent(TapInfo const & tapInfo)
{
if (m_tapEventInfoHandler != nullptr)
m_tapEventInfoHandler(tapInfo);
}
void DrapeEngine::UserPositionChanged(m2::PointD const & position, bool hasPosition)
{
if (m_userPositionChangedHandler != nullptr)
m_userPositionChangedHandler(position, hasPosition);
}
void DrapeEngine::ResizeImpl(int w, int h)
{
gui::DrapeGui::Instance().SetSurfaceSize(m2::PointF(w, h));
m_viewport.SetViewport(0, 0, w, h);
PostUserEvent(make_unique_dp<ResizeEvent>(w, h));
}
void DrapeEngine::SetCompassInfo(location::CompassInfo const & info)
{
m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread, make_unique_dp<CompassInfoMessage>(info),
MessagePriority::Normal);
}
void DrapeEngine::SetGpsInfo(location::GpsInfo const & info, bool isNavigable,
location::RouteMatchingInfo const & routeInfo)
{
m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<GpsInfoMessage>(info, isNavigable, routeInfo),
MessagePriority::Normal);
}
void DrapeEngine::SwitchMyPositionNextMode()
{
using Mode = ChangeMyPositionModeMessage::EChangeType;
m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<ChangeMyPositionModeMessage>(Mode::SwitchNextMode),
MessagePriority::Normal);
}
void DrapeEngine::LoseLocation()
{
using Mode = ChangeMyPositionModeMessage::EChangeType;
m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<ChangeMyPositionModeMessage>(Mode::LoseLocation),
MessagePriority::Normal);
}
void DrapeEngine::StopLocationFollow()
{
using Mode = ChangeMyPositionModeMessage::EChangeType;
m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<ChangeMyPositionModeMessage>(Mode::StopFollowing),
MessagePriority::Normal);
}
void DrapeEngine::FollowRoute(int preferredZoomLevel, int preferredZoomLevel3d, bool enableAutoZoom, bool isArrowGlued)
{
m_threadCommutator->PostMessage(
ThreadsCommutator::RenderThread,
make_unique_dp<FollowRouteMessage>(preferredZoomLevel, preferredZoomLevel3d, enableAutoZoom, isArrowGlued),
MessagePriority::Normal);
}
void DrapeEngine::SetModelViewListener(ModelViewChangedHandler && fn)
{
m_modelViewChangedHandler = std::move(fn);
}
#if defined(OMIM_OS_DESKTOP)
void DrapeEngine::NotifyGraphicsReady(GraphicsReadyHandler const & fn, bool needInvalidate)
{
m_threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
make_unique_dp<NotifyGraphicsReadyMessage>(fn, needInvalidate),
MessagePriority::Normal);
}
#endif
void DrapeEngine::SetTapEventInfoListener(TapEventInfoHandler && fn)
{
m_tapEventInfoHandler = std::move(fn);
}
void DrapeEngine::SetUserPositionListener(UserPositionChangedHandler && fn)
{
m_userPositionChangedHandler = std::move(fn);
}
void DrapeEngine::SelectObject(SelectionShape::ESelectedObject obj, m2::PointD const & pt, FeatureID const & featureId,
bool isAnim, bool isGeometrySelectionAllowed, bool isSelectionShapeVisible)
{
m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<SelectObjectMessage>(
obj, pt, featureId, isAnim, isGeometrySelectionAllowed, isSelectionShapeVisible),
MessagePriority::Normal);
}
void DrapeEngine::DeselectObject(bool restoreViewport)
{
m_threadCommutator->PostMessage(
ThreadsCommutator::RenderThread,
make_unique_dp<SelectObjectMessage>(SelectObjectMessage::DismissTag(), restoreViewport), MessagePriority::Normal);
}
dp::DrapeID DrapeEngine::AddSubroute(SubrouteConstPtr subroute)
{
dp::DrapeID const id = GenerateDrapeID();
m_threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
make_unique_dp<AddSubrouteMessage>(id, subroute), MessagePriority::Normal);
return id;
}
void DrapeEngine::RemoveSubroute(dp::DrapeID subrouteId, bool deactivateFollowing)
{
m_threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
make_unique_dp<RemoveSubrouteMessage>(subrouteId, deactivateFollowing),
MessagePriority::Normal);
}
void DrapeEngine::DeactivateRouteFollowing()
{
m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread, make_unique_dp<DeactivateRouteFollowingMessage>(),
MessagePriority::Normal);
}
void DrapeEngine::SetSubrouteVisibility(dp::DrapeID subrouteId, bool isVisible)
{
m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<SetSubrouteVisibilityMessage>(subrouteId, isVisible),
MessagePriority::Normal);
}
dp::DrapeID DrapeEngine::AddRoutePreviewSegment(m2::PointD const & startPt, m2::PointD const & finishPt)
{
dp::DrapeID const id = GenerateDrapeID();
m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<AddRoutePreviewSegmentMessage>(id, startPt, finishPt),
MessagePriority::Normal);
return id;
}
void DrapeEngine::RemoveRoutePreviewSegment(dp::DrapeID segmentId)
{
m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<RemoveRoutePreviewSegmentMessage>(segmentId), MessagePriority::Normal);
}
void DrapeEngine::RemoveAllRoutePreviewSegments()
{
m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread, make_unique_dp<RemoveRoutePreviewSegmentMessage>(),
MessagePriority::Normal);
}
void DrapeEngine::SetWidgetLayout(gui::TWidgetsLayoutInfo && info)
{
m_widgetsLayout = std::move(info);
for (auto const & layout : m_widgetsLayout)
{
auto const itInfo = m_widgetsInfo.find(layout.first);
if (itInfo != m_widgetsInfo.end())
itInfo->second.m_pixelPivot = layout.second;
}
m_threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
make_unique_dp<GuiLayerLayoutMessage>(m_widgetsLayout), MessagePriority::Normal);
}
void DrapeEngine::AllowAutoZoom(bool allowAutoZoom)
{
m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread, make_unique_dp<AllowAutoZoomMessage>(allowAutoZoom),
MessagePriority::Normal);
}
void DrapeEngine::Allow3dMode(bool allowPerspectiveInNavigation, bool allow3dBuildings)
{
m_threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
make_unique_dp<Allow3dBuildingsMessage>(allow3dBuildings), MessagePriority::Normal);
m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<Allow3dModeMessage>(allowPerspectiveInNavigation, allow3dBuildings),
MessagePriority::Normal);
}
void DrapeEngine::SetMapLangIndex(int8_t mapLangIndex)
{
m_threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
make_unique_dp<SetMapLangIndexMessage>(mapLangIndex), MessagePriority::Normal);
m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread, make_unique_dp<SetMapLangIndexMessage>(mapLangIndex),
MessagePriority::Normal);
}
void DrapeEngine::EnablePerspective()
{
m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread, make_unique_dp<EnablePerspectiveMessage>(),
MessagePriority::Normal);
}
void DrapeEngine::UpdateGpsTrackPoints(std::vector<df::GpsTrackPoint> && toAdd, std::vector<uint32_t> && toRemove)
{
m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<UpdateGpsTrackPointsMessage>(std::move(toAdd), std::move(toRemove)),
MessagePriority::Normal);
}
void DrapeEngine::ClearGpsTrackPoints()
{
m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread, make_unique_dp<ClearGpsTrackPointsMessage>(),
MessagePriority::Normal);
}
void DrapeEngine::EnableChoosePositionMode(bool enable, std::vector<m2::TriangleD> && boundAreaTriangles,
m2::PointD const * optionalPosition)
{
m_choosePositionMode = enable;
bool kineticScroll = m_kineticScrollEnabled;
if (enable)
{
StopLocationFollow();
m_threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
make_unique_dp<ShowChoosePositionMarkMessage>(), MessagePriority::Normal);
kineticScroll = false;
}
else
{
RecacheGui(true);
}
m_threadCommutator->PostMessage(
ThreadsCommutator::RenderThread,
make_unique_dp<SetAddNewPlaceModeMessage>(enable, std::move(boundAreaTriangles), kineticScroll, optionalPosition),
MessagePriority::Normal);
}
void DrapeEngine::BlockTapEvents(bool block)
{
m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread, make_unique_dp<BlockTapEventsMessage>(block),
MessagePriority::Normal);
}
void DrapeEngine::SetKineticScrollEnabled(bool enabled)
{
m_kineticScrollEnabled = enabled;
if (m_choosePositionMode)
return;
m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<SetKineticScrollEnabledMessage>(m_kineticScrollEnabled),
MessagePriority::Normal);
}
void DrapeEngine::OnEnterForeground()
{
double const backgroundTime = base::Timer::LocalTime() - m_startBackgroundTime;
m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<OnEnterForegroundMessage>(backgroundTime), MessagePriority::High);
}
void DrapeEngine::OnEnterBackground()
{
m_startBackgroundTime = base::Timer::LocalTime();
settings::Set(kLastEnterBackground, m_startBackgroundTime);
/// @todo By VNG: Make direct call to FR, because logic with PostMessage is not working now.
/// Rendering engine becomes disabled first and posted message won't be processed in a correct timing
/// and will remain pending in queue, waiting until rendering queue will became active.
/// As a result, we will get OnEnterBackground notification when we already entered foreground (sic!).
/// One minus with direct call is that we are not in FR rendering thread, but I don't see a problem here now.
/// To make it works as expected with PostMessage, we should refactor platform notifications,
/// especially Android with its AppBackgroundTracker.
m_frontend->OnEnterBackground();
// m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread,
// make_unique_dp<OnEnterBackgroundMessage>(),
// MessagePriority::High);
}
void DrapeEngine::RequestSymbolsSize(std::vector<std::string> const & symbols,
TRequestSymbolsSizeCallback const & callback)
{
m_threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
make_unique_dp<RequestSymbolsSizeMessage>(symbols, callback),
MessagePriority::Normal);
}
void DrapeEngine::EnableTraffic(bool trafficEnabled)
{
m_threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
make_unique_dp<EnableTrafficMessage>(trafficEnabled), MessagePriority::Normal);
}
void DrapeEngine::UpdateTraffic(traffic::TrafficInfo const & info)
{
if (info.GetColoring().empty())
return;
#ifdef DEBUG
for (auto const & segmentPair : info.GetColoring())
ASSERT_NOT_EQUAL(segmentPair.second, traffic::SpeedGroup::Unknown, ());
#endif
df::TrafficSegmentsColoring segmentsColoring;
segmentsColoring.emplace(info.GetMwmId(), info.GetColoring());
m_threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
make_unique_dp<UpdateTrafficMessage>(std::move(segmentsColoring)),
MessagePriority::Normal);
}
void DrapeEngine::ClearTrafficCache(MwmSet::MwmId const & mwmId)
{
m_threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
make_unique_dp<ClearTrafficDataMessage>(mwmId), MessagePriority::Normal);
}
void DrapeEngine::SetSimplifiedTrafficColors(bool simplified)
{
m_threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
make_unique_dp<SetSimplifiedTrafficColorsMessage>(simplified),
MessagePriority::Normal);
}
void DrapeEngine::EnableTransitScheme(bool enable)
{
m_threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
make_unique_dp<EnableTransitSchemeMessage>(enable), MessagePriority::Normal);
}
void DrapeEngine::ClearTransitSchemeCache(MwmSet::MwmId const & mwmId)
{
m_threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
make_unique_dp<ClearTransitSchemeDataMessage>(mwmId), MessagePriority::Normal);
}
void DrapeEngine::ClearAllTransitSchemeCache()
{
m_threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
make_unique_dp<ClearAllTransitSchemeDataMessage>(), MessagePriority::Normal);
}
void DrapeEngine::UpdateTransitScheme(TransitDisplayInfos && transitDisplayInfos)
{
m_threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
make_unique_dp<UpdateTransitSchemeMessage>(std::move(transitDisplayInfos)),
MessagePriority::Normal);
}
void DrapeEngine::EnableIsolines(bool enable)
{
m_threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
make_unique_dp<EnableIsolinesMessage>(enable), MessagePriority::Normal);
}
void DrapeEngine::SetFontScaleFactor(double scaleFactor)
{
VisualParams::Instance().SetFontScale(scaleFactor);
}
void DrapeEngine::RunScenario(ScenarioManager::ScenarioData && scenarioData,
ScenarioManager::ScenarioCallback const & onStartFn,
ScenarioManager::ScenarioCallback const & onFinishFn)
{
auto const & manager = m_frontend->GetScenarioManager();
if (manager != nullptr)
manager->RunScenario(std::move(scenarioData), onStartFn, onFinishFn);
}
void DrapeEngine::SetCustomFeatures(df::CustomFeatures && ids)
{
m_threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
make_unique_dp<SetCustomFeaturesMessage>(std::move(ids)), MessagePriority::Normal);
}
void DrapeEngine::RemoveCustomFeatures(MwmSet::MwmId const & mwmId)
{
m_threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
make_unique_dp<RemoveCustomFeaturesMessage>(mwmId), MessagePriority::Normal);
}
void DrapeEngine::RemoveAllCustomFeatures()
{
m_threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
make_unique_dp<RemoveCustomFeaturesMessage>(), MessagePriority::Normal);
}
void DrapeEngine::SetPosteffectEnabled(PostprocessRenderer::Effect effect, bool enabled)
{
if (effect == df::PostprocessRenderer::Antialiasing)
{
LOG(LINFO, ("Antialiasing is", (enabled ? "enabled" : "disabled")));
settings::Set(dp::kSupportedAntialiasing, enabled);
}
m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<SetPosteffectEnabledMessage>(effect, enabled),
MessagePriority::Normal);
}
void DrapeEngine::RunFirstLaunchAnimation()
{
m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread, make_unique_dp<RunFirstLaunchAnimationMessage>(),
MessagePriority::Normal);
}
void DrapeEngine::ShowDebugInfo(bool shown)
{
m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread, make_unique_dp<ShowDebugInfoMessage>(shown),
MessagePriority::Normal);
}
void DrapeEngine::EnableDebugRectRendering(bool enabled)
{
m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<EnableDebugRectRenderingMessage>(enabled), MessagePriority::Normal);
}
drape_ptr<UserMarkRenderParams> DrapeEngine::GenerateMarkRenderInfo(UserPointMark const * mark)
{
auto renderInfo = make_unique_dp<UserMarkRenderParams>();
renderInfo->m_markId = mark->GetId();
renderInfo->m_anchor = mark->GetAnchor();
renderInfo->m_depthTestEnabled = mark->GetDepthTestEnabled();
if (mark->GetDepth() != UserPointMark::kInvalidDepth)
{
renderInfo->m_depth = mark->GetDepth();
renderInfo->m_customDepth = true;
}
renderInfo->m_depthLayer = mark->GetDepthLayer();
renderInfo->m_minZoom = mark->GetMinZoom();
renderInfo->m_minTitleZoom = mark->GetMinTitleZoom();
renderInfo->m_isVisible = mark->IsVisible();
renderInfo->m_pivot = mark->GetPivot();
renderInfo->m_pixelOffset = mark->GetPixelOffset();
renderInfo->m_titleDecl = mark->GetTitleDecl();
renderInfo->m_symbolNames = mark->GetSymbolNames();
renderInfo->m_coloredSymbols = mark->GetColoredSymbols();
renderInfo->m_symbolSizes = mark->GetSymbolSizes();
renderInfo->m_symbolOffsets = mark->GetSymbolOffsets();
renderInfo->m_color = mark->GetColorConstant();
renderInfo->m_symbolIsPOI = mark->SymbolIsPOI();
renderInfo->m_hasTitlePriority = mark->HasTitlePriority();
renderInfo->m_priority = mark->GetPriority();
renderInfo->m_displacement = mark->GetDisplacement();
renderInfo->m_index = mark->GetIndex();
renderInfo->m_featureId = mark->GetFeatureID();
renderInfo->m_hasCreationAnimation = mark->HasCreationAnimation();
renderInfo->m_isMarkAboveText = mark->IsMarkAboveText();
renderInfo->m_symbolOpacity = mark->GetSymbolOpacity();
renderInfo->m_isSymbolSelectable = mark->IsSymbolSelectable();
renderInfo->m_isNonDisplaceable = mark->IsNonDisplaceable();
return renderInfo;
}
drape_ptr<UserLineRenderParams> DrapeEngine::GenerateLineRenderInfo(UserLineMark const * mark)
{
auto renderInfo = make_unique_dp<UserLineRenderParams>();
renderInfo->m_minZoom = mark->GetMinZoom();
renderInfo->m_depthLayer = mark->GetDepthLayer();
mark->ForEachGeometry([&renderInfo](std::vector<m2::PointD> && points)
{ renderInfo->m_splines.emplace_back(std::move(points)); });
renderInfo->m_layers.reserve(mark->GetLayerCount());
for (size_t layerIndex = 0, layersCount = mark->GetLayerCount(); layerIndex < layersCount; ++layerIndex)
{
renderInfo->m_layers.emplace_back(mark->GetColor(layerIndex), mark->GetWidth(layerIndex),
mark->GetDepth(layerIndex));
}
return renderInfo;
}
void DrapeEngine::UpdateVisualScale(double vs, bool needStopRendering)
{
if (needStopRendering)
SetRenderingDisabled(true /* destroySurface */);
VisualParams::Instance().SetVisualScale(vs);
if (needStopRendering)
SetRenderingEnabled();
RecacheGui(false);
RecacheMapShapes();
m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<RecoverContextDependentResourcesMessage>(), MessagePriority::Normal);
}
void DrapeEngine::UpdateMyPositionRoutingOffset(bool useDefault, int offsetY)
{
m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread,
make_unique_dp<UpdateMyPositionRoutingOffsetMessage>(useDefault, offsetY),
MessagePriority::Normal);
}
void DrapeEngine::SetCustomArrow3d(std::optional<Arrow3dCustomDecl> arrow3dCustomDecl)
{
m_threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
make_unique_dp<Arrow3dRecacheMessage>(std::move(arrow3dCustomDecl)),
MessagePriority::High);
}
} // namespace df

View file

@ -0,0 +1,290 @@
#pragma once
#include "traffic/traffic_info.hpp"
#include "drape_frontend/backend_renderer.hpp"
#include "drape_frontend/color_constants.hpp"
#include "drape_frontend/custom_features_context.hpp"
#include "drape_frontend/drape_engine_params.hpp"
#include "drape_frontend/drape_hints.hpp"
#include "drape_frontend/frontend_renderer.hpp"
#include "drape_frontend/overlays_tracker.hpp"
#include "drape_frontend/postprocess_renderer.hpp"
#include "drape_frontend/route_shape.hpp"
#include "drape_frontend/scenario_manager.hpp"
#include "drape_frontend/selection_shape.hpp"
#include "drape_frontend/threads_commutator.hpp"
#include "drape/drape_global.hpp"
#include "drape/pointers.hpp"
#include "drape/texture_manager.hpp"
#include "drape/viewport.hpp"
#include "transit/transit_display_info.hpp"
#include "platform/location.hpp"
#include "geometry/polyline2d.hpp"
#include "geometry/screenbase.hpp"
#include "geometry/triangle2d.hpp"
#include "base/strings_bundle.hpp"
#include <atomic>
#include <functional>
#include <map>
#include <utility>
#include <vector>
namespace dp
{
class GlyphGenerator;
class GraphicsContextFactory;
} // namespace dp
namespace df
{
class UserMarksProvider;
class MapDataProvider;
class DrapeEngine
{
public:
struct Params
{
Params(dp::ApiVersion apiVersion, ref_ptr<dp::GraphicsContextFactory> factory, dp::Viewport const & viewport,
MapDataProvider const & model, Hints const & hints, double vs, double fontsScaleFactor,
gui::TWidgetsInitInfo && info, location::TMyPositionModeChanged && myPositionModeChanged,
bool allow3dBuildings, bool trafficEnabled, bool isolinesEnabled, bool blockTapEvents,
bool showChoosePositionMark, std::vector<m2::TriangleD> && boundAreaTriangles, bool isRoutingActive,
bool isAutozoomEnabled, bool simplifiedTrafficColors, std::optional<Arrow3dCustomDecl> arrow3dCustomDecl,
OverlaysShowStatsCallback && overlaysShowStatsCallback,
OnGraphicsContextInitialized && onGraphicsContextInitialized,
dp::RenderInjectionHandler && renderInjectionHandler)
: m_apiVersion(apiVersion)
, m_factory(factory)
, m_viewport(viewport)
, m_model(model)
, m_hints(hints)
, m_vs(vs)
, m_fontsScaleFactor(fontsScaleFactor)
, m_info(std::move(info))
, m_myPositionModeChanged(std::move(myPositionModeChanged))
, m_allow3dBuildings(allow3dBuildings)
, m_trafficEnabled(trafficEnabled)
, m_isolinesEnabled(isolinesEnabled)
, m_blockTapEvents(blockTapEvents)
, m_showChoosePositionMark(showChoosePositionMark)
, m_boundAreaTriangles(std::move(boundAreaTriangles))
, m_isRoutingActive(isRoutingActive)
, m_isAutozoomEnabled(isAutozoomEnabled)
, m_simplifiedTrafficColors(simplifiedTrafficColors)
, m_arrow3dCustomDecl(std::move(arrow3dCustomDecl))
, m_overlaysShowStatsCallback(std::move(overlaysShowStatsCallback))
, m_onGraphicsContextInitialized(std::move(onGraphicsContextInitialized))
, m_renderInjectionHandler(std::move(renderInjectionHandler))
{}
dp::ApiVersion m_apiVersion;
ref_ptr<dp::GraphicsContextFactory> m_factory;
dp::Viewport m_viewport;
MapDataProvider m_model;
Hints m_hints;
double m_vs;
double m_fontsScaleFactor;
gui::TWidgetsInitInfo m_info;
std::pair<location::EMyPositionMode, bool> m_initialMyPositionMode;
location::TMyPositionModeChanged m_myPositionModeChanged;
bool m_allow3dBuildings;
bool m_trafficEnabled;
bool m_isolinesEnabled;
bool m_blockTapEvents;
bool m_showChoosePositionMark;
std::vector<m2::TriangleD> m_boundAreaTriangles;
bool m_isRoutingActive;
bool m_isAutozoomEnabled;
bool m_simplifiedTrafficColors;
std::optional<Arrow3dCustomDecl> m_arrow3dCustomDecl;
OverlaysShowStatsCallback m_overlaysShowStatsCallback;
OnGraphicsContextInitialized m_onGraphicsContextInitialized;
dp::RenderInjectionHandler m_renderInjectionHandler;
};
DrapeEngine(Params && params);
~DrapeEngine();
void RecoverSurface(int w, int h, bool recreateContextDependentResources);
void Resize(int w, int h);
void Invalidate();
void SetVisibleViewport(m2::RectD const & rect) const;
void AddTouchEvent(TouchEvent const & event);
void Scale(double factor, m2::PointD const & pxPoint, bool isAnim);
void Move(double factorX, double factorY, bool isAnim);
void Scroll(double distanceX, double distanceY);
void Rotate(double azimuth, bool isAnim);
void MakeFrameActive();
void ScaleAndSetCenter(m2::PointD const & centerPt, double scaleFactor, bool isAnim, bool trackVisibleViewport);
// If zoom == -1 then current zoom will not be changed.
void SetModelViewCenter(m2::PointD const & centerPt, int zoom, bool isAnim, bool trackVisibleViewport);
void SetModelViewRect(m2::RectD const & rect, bool applyRotation, int zoom, bool isAnim, bool useVisibleViewport);
void SetModelViewAnyRect(m2::AnyRectD const & rect, bool isAnim, bool useVisibleViewport);
using ModelViewChangedHandler = FrontendRenderer::ModelViewChangedHandler;
void SetModelViewListener(ModelViewChangedHandler && fn);
#if defined(OMIM_OS_DESKTOP)
using GraphicsReadyHandler = FrontendRenderer::GraphicsReadyHandler;
void NotifyGraphicsReady(GraphicsReadyHandler const & fn, bool needInvalidate);
#endif
void ClearUserMarksGroup(kml::MarkGroupId groupId);
void ChangeVisibilityUserMarksGroup(kml::MarkGroupId groupId, bool isVisible);
void UpdateUserMarks(UserMarksProvider * provider, bool firstTime);
void InvalidateUserMarks();
void SetRenderingEnabled(ref_ptr<dp::GraphicsContextFactory> contextFactory = nullptr);
void SetRenderingDisabled(bool const destroySurface);
void InvalidateRect(m2::RectD const & rect);
void UpdateMapStyle();
void SetCompassInfo(location::CompassInfo const & info);
void SetGpsInfo(location::GpsInfo const & info, bool isNavigable, location::RouteMatchingInfo const & routeInfo);
void SwitchMyPositionNextMode();
void LoseLocation();
void StopLocationFollow();
using TapEventInfoHandler = FrontendRenderer::TapEventInfoHandler;
void SetTapEventInfoListener(TapEventInfoHandler && fn);
using UserPositionChangedHandler = FrontendRenderer::UserPositionChangedHandler;
void SetUserPositionListener(UserPositionChangedHandler && fn);
void SelectObject(SelectionShape::ESelectedObject obj, m2::PointD const & pt, FeatureID const & featureID,
bool isAnim, bool isGeometrySelectionAllowed, bool isSelectionShapeVisible);
void DeselectObject(bool restoreViewport);
dp::DrapeID AddSubroute(SubrouteConstPtr subroute);
void RemoveSubroute(dp::DrapeID subrouteId, bool deactivateFollowing);
void FollowRoute(int preferredZoomLevel, int preferredZoomLevel3d, bool enableAutoZoom, bool isArrowGlued);
void DeactivateRouteFollowing();
void SetSubrouteVisibility(dp::DrapeID subrouteId, bool isVisible);
dp::DrapeID AddRoutePreviewSegment(m2::PointD const & startPt, m2::PointD const & finishPt);
void RemoveRoutePreviewSegment(dp::DrapeID segmentId);
void RemoveAllRoutePreviewSegments();
void SetWidgetLayout(gui::TWidgetsLayoutInfo && info);
void AllowAutoZoom(bool allowAutoZoom);
void Allow3dMode(bool allowPerspectiveInNavigation, bool allow3dBuildings);
void EnablePerspective();
void UpdateGpsTrackPoints(std::vector<df::GpsTrackPoint> && toAdd, std::vector<uint32_t> && toRemove);
void ClearGpsTrackPoints();
void EnableChoosePositionMode(bool enable, std::vector<m2::TriangleD> && boundAreaTriangles,
m2::PointD const * optionalPosition);
void BlockTapEvents(bool block);
void SetKineticScrollEnabled(bool enabled);
void SetMapLangIndex(int8_t mapLangIndex);
void OnEnterForeground();
void OnEnterBackground();
using TRequestSymbolsSizeCallback = std::function<void(std::map<std::string, m2::PointF> &&)>;
void RequestSymbolsSize(std::vector<std::string> const & symbols, TRequestSymbolsSizeCallback const & callback);
void EnableTraffic(bool trafficEnabled);
void UpdateTraffic(traffic::TrafficInfo const & info);
void ClearTrafficCache(MwmSet::MwmId const & mwmId);
void SetSimplifiedTrafficColors(bool simplified);
void EnableTransitScheme(bool enable);
void UpdateTransitScheme(TransitDisplayInfos && transitDisplayInfos);
void ClearTransitSchemeCache(MwmSet::MwmId const & mwmId);
void ClearAllTransitSchemeCache();
void EnableIsolines(bool enable);
void SetFontScaleFactor(double scaleFactor);
void RunScenario(ScenarioManager::ScenarioData && scenarioData, ScenarioManager::ScenarioCallback const & onStartFn,
ScenarioManager::ScenarioCallback const & onFinishFn);
/// @name Custom features are features that we render in a different way.
/// Value in the map shows if the feature is skipped in process of geometry generation.
/// For all custom features (if they are overlays) statistics will be gathered.
/// @todo Not used now, suspect that it was used for some Ads POIs.
/// @{
void SetCustomFeatures(df::CustomFeatures && ids);
void RemoveCustomFeatures(MwmSet::MwmId const & mwmId);
void RemoveAllCustomFeatures();
/// @}
void SetPosteffectEnabled(PostprocessRenderer::Effect effect, bool enabled);
void EnableDebugRectRendering(bool enabled);
void RunFirstLaunchAnimation();
void ShowDebugInfo(bool shown);
void UpdateVisualScale(double vs, bool needStopRendering);
void UpdateMyPositionRoutingOffset(bool useDefault, int offsetY);
location::EMyPositionMode GetMyPositionMode() const;
void SetCustomArrow3d(std::optional<Arrow3dCustomDecl> arrow3dCustomDecl);
dp::ApiVersion GetApiVersion() const { return m_frontend->GetApiVersion(); }
private:
void AddUserEvent(drape_ptr<UserEvent> && e);
void PostUserEvent(drape_ptr<UserEvent> && e);
void ModelViewChanged(ScreenBase const & screen);
void MyPositionModeChanged(location::EMyPositionMode mode, bool routingActive);
void TapEvent(TapInfo const & tapInfo);
void UserPositionChanged(m2::PointD const & position, bool hasPosition);
void ResizeImpl(int w, int h);
void RecacheGui(bool needResetOldGui);
void RecacheMapShapes();
dp::DrapeID GenerateDrapeID();
static drape_ptr<UserMarkRenderParams> GenerateMarkRenderInfo(UserPointMark const * mark);
static drape_ptr<UserLineRenderParams> GenerateLineRenderInfo(UserLineMark const * mark);
drape_ptr<FrontendRenderer> m_frontend;
drape_ptr<BackendRenderer> m_backend;
drape_ptr<ThreadsCommutator> m_threadCommutator;
drape_ptr<dp::TextureManager> m_textureManager;
drape_ptr<RequestedTiles> m_requestedTiles;
location::TMyPositionModeChanged m_myPositionModeChanged;
dp::Viewport m_viewport;
ModelViewChangedHandler m_modelViewChangedHandler;
TapEventInfoHandler m_tapEventInfoHandler;
UserPositionChangedHandler m_userPositionChangedHandler;
gui::TWidgetsInitInfo m_widgetsInfo;
gui::TWidgetsLayoutInfo m_widgetsLayout;
bool m_choosePositionMode = false;
bool m_kineticScrollEnabled = true;
std::atomic<dp::DrapeID> m_drapeIdGenerator = 0;
double m_startBackgroundTime = 0;
friend class DrapeApi;
};
} // namespace df

View file

@ -0,0 +1,47 @@
#pragma once
#include "geometry/point3d.hpp"
#include <string>
namespace df
{
// Declaration for custom Arrow3D object.
struct Arrow3dCustomDecl
{
// Path to arrow mesh .OBJ file.
std::string m_arrowMeshPath;
// Path to arrow mesh texture .PNG file.
// If it's empty, default arrow color is used.
std::string m_arrowMeshTexturePath;
// Path to shadow mesh .OBJ file.
// If it's empty, no shadow or outline will be rendered.
std::string m_shadowMeshPath;
// Allows to load files from bundled resources.
// You must use string identifiers of resources instead of file names.
bool m_loadFromDefaultResourceFolder = false;
// Layout of axes (in the plane of map): x - right, y - up,
// -z - perpendicular to the map's plane directed towards the observer.
// Offset is in local (model's) coordinates.
m3::PointF m_offset = m3::PointF(0.0f, 0.0f, 0.0f);
// Rotation angles.
m3::PointF m_eulerAngles = m3::PointF(0.0f, 0.0f, 0.0f);
// Scale values.
m3::PointF m_scale = m3::PointF(1.0f, 1.0f, 1.0f);
// Flip U texture coordinate.
bool m_flipTexCoordU = false;
// Flip V texture coordinate (enabled in the Drape by default).
bool m_flipTexCoordV = true;
// Enable shadow rendering (only in perspective mode).
// Shadow mesh must exist, otherwise standard one will be used.
bool m_enableShadow = false;
// Enabled outline rendering (only in routing mode).
// Shadow mesh must exist, otherwise standard one will be used.
bool m_enableOutline = false;
};
} // namespace df

View file

@ -0,0 +1,68 @@
#pragma once
#include "base/macros.hpp"
#include "drape/drape_global.hpp"
#include "drape/pointers.hpp"
#include <mutex>
namespace df
{
class DrapeEngine;
class DrapeEngineSafePtr
{
public:
void Set(ref_ptr<DrapeEngine> engine)
{
std::lock_guard<std::mutex> lock(m_mutex);
m_engine = engine;
}
explicit operator bool()
{
std::lock_guard<std::mutex> lock(m_mutex);
return m_engine != nullptr;
}
template <typename Function, typename... Args>
void SafeCall(Function && f, Args &&... functionArgs)
{
std::lock_guard<std::mutex> lock(m_mutex);
if (m_engine != nullptr)
(m_engine.get()->*f)(std::forward<Args>(functionArgs)...);
}
template <typename Function, typename... Args>
dp::DrapeID SafeCallWithResult(Function && f, Args &&... functionArgs)
{
std::lock_guard<std::mutex> lock(m_mutex);
if (m_engine != nullptr)
return (m_engine.get()->*f)(std::forward<Args>(functionArgs)...);
return dp::DrapeID();
}
private:
ref_ptr<DrapeEngine> m_engine;
std::mutex m_mutex;
friend class DrapeEngineLockGuard;
};
class DrapeEngineLockGuard
{
public:
explicit DrapeEngineLockGuard(DrapeEngineSafePtr & enginePtr) : m_ptr(enginePtr) { m_ptr.m_mutex.lock(); }
~DrapeEngineLockGuard() { m_ptr.m_mutex.unlock(); }
explicit operator bool() { return m_ptr.m_engine != nullptr; }
ref_ptr<DrapeEngine> Get() { return m_ptr.m_engine; }
private:
DrapeEngineSafePtr & m_ptr;
DISALLOW_COPY_AND_MOVE(DrapeEngineLockGuard);
};
} // namespace df

View file

@ -0,0 +1,13 @@
project(drape_frontend_tests)
set(SRC
frame_values_tests.cpp
navigator_test.cpp
path_text_test.cpp
stylist_tests.cpp
user_event_stream_tests.cpp
)
omim_add_test(${PROJECT_NAME} ${SRC})
target_link_libraries(${PROJECT_NAME} drape_frontend)

View file

@ -0,0 +1,42 @@
#include "testing/testing.hpp"
#include "drape_frontend/frame_values.hpp"
#include "drape/glsl_types.hpp"
#include "shaders/program_params.hpp"
namespace
{
glsl::mat4 const kTestMatrix1 =
glsl::mat4(1.0f, 2.0f, 3.0f, 4.0f, 1.0f, 2.0f, 3.0f, 4.0f, 1.0f, 2.0f, 3.0f, 4.0f, 1.0f, 2.0f, 3.0f, 4.0f);
glsl::mat4 const kTestMatrix2 =
glsl::mat4(4.0f, 3.0f, 2.0f, 1.0f, 4.0f, 3.0f, 2.0f, 1.0f, 4.0f, 3.0f, 2.0f, 1.0f, 4.0f, 3.0f, 2.0f, 1.0f);
} // namespace
UNIT_TEST(FrameValues_SetTo)
{
df::FrameValues frameValues;
frameValues.m_pivotTransform = kTestMatrix1;
frameValues.m_projection = kTestMatrix2;
frameValues.m_zScale = 42.0f;
gpu::MapProgramParams params;
frameValues.SetTo(params);
TEST(params.m_pivotTransform == frameValues.m_pivotTransform, ());
TEST(params.m_projection == frameValues.m_projection, ());
TEST(params.m_zScale == frameValues.m_zScale, ());
}
UNIT_TEST(FrameValues_SetTo_Partial)
{
df::FrameValues frameValues;
frameValues.m_pivotTransform = kTestMatrix1;
frameValues.m_projection = kTestMatrix2;
gpu::RouteProgramParams params;
frameValues.SetTo(params);
TEST(params.m_pivotTransform == frameValues.m_pivotTransform, ());
TEST(params.m_projection == frameValues.m_projection, ());
}

View file

@ -0,0 +1,84 @@
#include "testing/testing.hpp"
#include "drape_frontend/navigator.hpp"
#include "drape_frontend/visual_params.hpp"
#include "geometry/screenbase.hpp"
#include <cmath>
// -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
//
// 7 +-----------------------------------------------+
// | |
// 6 | |
// | |
// 5 | |
// | |
// 4 | +-----------------------+ |
// | | | |
// 3 | | | |
// | | | |
// 2 | | | |
// | | | |
// 1 | | a B<-------b | |
// | | | |
// 0 | +-----------------------+ |
// | |
//-1 +-----------------------------------------------+
UNIT_TEST(Navigator_Scale2Points)
{
df::VisualParams::Init(1.0, 1024);
df::Navigator navigator;
navigator.OnSize(200, 100);
navigator.SetFromRect(m2::AnyRectD(m2::RectD(0, 0, 8, 4)));
ScreenBase const & screen = navigator.Screen();
TEST_EQUAL(screen.ClipRect(), m2::RectD(0, 0, 8, 4), ());
navigator.StartScale(screen.GtoP(m2::PointD(1, 1)), screen.GtoP(m2::PointD(7, 1)));
navigator.StopScale(screen.GtoP(m2::PointD(1, 1)), screen.GtoP(m2::PointD(4, 1)));
TEST_EQUAL(screen.ClipRect(), m2::RectD(-1, -1, 15, 7), ());
}
namespace
{
void CheckNavigator(df::Navigator const & nav)
{
typedef m2::PointD P;
m2::RectD clipR = nav.Screen().ClipRect();
P arr[] = {clipR.LeftTop(), clipR.RightTop(), clipR.RightBottom(), clipR.LeftBottom(), clipR.Center()};
for (size_t i = 0; i < ARRAY_SIZE(arr); ++i)
{
P const & pxP = arr[i];
P const gP = nav.PtoG(pxP);
P const pxP2 = nav.GtoP(gP);
TEST(AlmostEqualAbs(pxP.x, pxP2.x, 0.00001), (pxP.x, pxP2.x));
TEST(AlmostEqualAbs(pxP.y, pxP2.y, 0.00001), (pxP.y, pxP2.y));
}
}
} // namespace
UNIT_TEST(Navigator_G2P_P2G)
{
df::VisualParams::Init(1.0, 1024);
df::Navigator navigator;
// Initialize.
navigator.OnSize(200, 100);
m2::PointD center = navigator.Screen().PixelRect().Center();
navigator.SetFromRect(m2::AnyRectD(m2::RectD(0, 0, 8, 4)));
TEST_EQUAL(navigator.Screen().ClipRect(), m2::RectD(0, 0, 8, 4), ());
CheckNavigator(navigator);
navigator.Scale(center, 3.0);
CheckNavigator(navigator);
navigator.Scale(center, 1 / 3.0);
CheckNavigator(navigator);
}

View file

@ -0,0 +1,58 @@
#include "testing/testing.hpp"
#include "drape_frontend/path_text_handle.hpp"
#include "drape_frontend/visual_params.hpp"
#include "base/logging.hpp"
namespace
{
bool IsSmooth(m2::SplineEx const & spline)
{
for (size_t i = 0, sz = spline.GetDirections().size(); i + 1 < sz; ++i)
if (!df::IsValidSplineTurn(spline.GetDirections()[i], spline.GetDirections()[i + 1]))
return false;
return true;
}
} // namespace
UNIT_TEST(Rounding_Spline)
{
df::VisualParams::Init(1.0, 1024);
m2::SplineEx spline1;
df::AddPointAndRound(spline1, m2::PointD(0, 200));
df::AddPointAndRound(spline1, m2::PointD(0, 0));
df::AddPointAndRound(spline1, m2::PointD(200, 0));
TEST(IsSmooth(spline1), ());
TEST(spline1.GetSize() == 8, ());
m2::SplineEx spline2;
df::AddPointAndRound(spline2, m2::PointD(-200, 0));
df::AddPointAndRound(spline2, m2::PointD(0, 0));
df::AddPointAndRound(spline2, m2::PointD(200, 200));
df::AddPointAndRound(spline2, m2::PointD(400, 200));
TEST(IsSmooth(spline2), ());
TEST(spline2.GetSize() == 8, ());
m2::SplineEx spline3;
df::AddPointAndRound(spline3, m2::PointD(200, 100));
df::AddPointAndRound(spline3, m2::PointD(0, 0));
df::AddPointAndRound(spline3, m2::PointD(200, 0));
TEST(!IsSmooth(spline3), ());
TEST(spline3.GetSize() == 3, ());
m2::SplineEx spline4;
df::AddPointAndRound(spline4, m2::PointD(-200, 5));
df::AddPointAndRound(spline4, m2::PointD(0, 0));
df::AddPointAndRound(spline4, m2::PointD(200, 5));
TEST(IsSmooth(spline4), ());
TEST(spline4.GetSize() == 3, ());
m2::SplineEx spline5;
df::AddPointAndRound(spline5, m2::PointD(200, 5));
df::AddPointAndRound(spline5, m2::PointD(0, 0));
df::AddPointAndRound(spline5, m2::PointD(200, -5));
TEST(!IsSmooth(spline5), ());
TEST(spline5.GetSize() == 3, ());
}

View file

@ -0,0 +1,24 @@
#include "testing/testing.hpp"
#include "drape_frontend/stylist.hpp"
#include "indexer/classificator.hpp"
#include "indexer/classificator_loader.hpp"
UNIT_TEST(Stylist_IsHatching)
{
classificator::Load();
auto const & cl = classif();
auto const & checker = df::IsHatchingTerritoryChecker::Instance();
TEST(checker(cl.GetTypeByPath({"boundary", "protected_area", "1"})), ());
TEST(!checker(cl.GetTypeByPath({"boundary", "protected_area", "2"})), ());
TEST(!checker(cl.GetTypeByPath({"boundary", "protected_area"})), ());
TEST(checker(cl.GetTypeByPath({"boundary", "national_park"})), ());
TEST(checker(cl.GetTypeByPath({"landuse", "military", "danger_area"})), ());
TEST(checker(cl.GetTypeByPath({"amenity", "prison"})), ());
}

View file

@ -0,0 +1,198 @@
#include "testing/testing.hpp"
#include "drape_frontend/user_event_stream.hpp"
#include "base/thread.hpp"
#include <cstring>
#include <functional>
#include <list>
using namespace std::placeholders;
#ifdef DEBUG
namespace
{
class UserEventStreamTest : df::UserEventStream::Listener
{
public:
explicit UserEventStreamTest(bool filtrateTouches) : m_filtrate(filtrateTouches)
{
m_stream.SetTestBridge(std::bind(&UserEventStreamTest::TestBridge, this, _1));
}
void OnTap(m2::PointD const & pt, bool isLong) override {}
void OnForceTap(m2::PointD const & pt) override {}
void OnDoubleTap(m2::PointD const & pt) override {}
void OnTwoFingersTap() override {}
bool OnSingleTouchFiltrate(m2::PointD const & pt, df::TouchEvent::ETouchType type) override { return m_filtrate; }
void OnDragStarted() override {}
void OnDragEnded(m2::PointD const & /* distance */) override {}
void OnRotated() override {}
void OnScrolled(m2::PointD const & distance) override {}
void OnScaleStarted() override {}
void CorrectScalePoint(m2::PointD & pt) const override {}
void CorrectScalePoint(m2::PointD & pt1, m2::PointD & pt2) const override {}
void CorrectGlobalScalePoint(m2::PointD & pt) const override {}
void OnScaleEnded() override {}
void OnTouchMapAction(df::TouchEvent::ETouchType touchType, bool isMapTouch) override {}
void OnAnimatedScaleEnded() override {}
bool OnNewVisibleViewport(m2::RectD const & oldViewport, m2::RectD const & newViewport, bool needOffset,
m2::PointD & gOffset) override
{
return false;
}
void AddUserEvent(df::TouchEvent const & event) { m_stream.AddEvent(make_unique_dp<df::TouchEvent>(event)); }
void SetRect(m2::RectD const & r)
{
m_stream.AddEvent(make_unique_dp<df::SetRectEvent>(r, false /* rotate */, -1, false /* isAnim */,
false /* useVisibleViewport */,
nullptr /* parallelAnimCreator */));
}
void AddExpectation(char const * action) { m_expectation.push_back(action); }
void RunTest()
{
bool dummy1, dummy2, dummy3;
m_stream.ProcessEvents(dummy1, dummy2, dummy3);
TEST_EQUAL(m_expectation.empty(), true, ());
}
private:
void TestBridge(char const * action)
{
TEST(!m_expectation.empty(), ());
char const * a = m_expectation.front();
TEST_EQUAL(strcmp(action, a), 0, ());
m_expectation.pop_front();
}
private:
df::UserEventStream m_stream;
std::list<char const *> m_expectation;
bool m_filtrate;
};
int touchTimeStamp = 1;
df::TouchEvent MakeTouchEvent(m2::PointD const & pt1, m2::PointD const & pt2, df::TouchEvent::ETouchType type)
{
df::TouchEvent e;
df::Touch t1;
t1.m_location = pt1;
t1.m_id = 1;
e.SetFirstTouch(t1);
df::Touch t2;
t2.m_location = pt2;
t2.m_id = 2;
e.SetSecondTouch(t2);
e.SetTouchType(type);
e.SetFirstMaskedPointer(0);
e.SetSecondMaskedPointer(1);
e.SetTimeStamp(touchTimeStamp++);
return e;
}
df::TouchEvent MakeTouchEvent(m2::PointD const & pt, df::TouchEvent::ETouchType type)
{
df::TouchEvent e;
df::Touch t1;
t1.m_location = pt;
t1.m_id = 1;
e.SetFirstTouch(t1);
e.SetTouchType(type);
e.SetFirstMaskedPointer(0);
e.SetTimeStamp(touchTimeStamp++);
return e;
}
} // namespace
UNIT_TEST(SimpleTap)
{
UserEventStreamTest test(false);
test.AddExpectation(df::UserEventStream::TRY_FILTER);
test.AddExpectation(df::UserEventStream::BEGIN_TAP_DETECTOR);
test.AddExpectation(df::UserEventStream::SHORT_TAP_DETECTED);
test.AddUserEvent(MakeTouchEvent(m2::PointD::Zero(), df::TouchEvent::TOUCH_DOWN));
test.AddUserEvent(MakeTouchEvent(m2::PointD::Zero(), df::TouchEvent::TOUCH_UP));
test.RunTest();
}
UNIT_TEST(SimpleLongTap)
{
UserEventStreamTest test(false);
test.AddExpectation(df::UserEventStream::TRY_FILTER);
test.AddExpectation(df::UserEventStream::BEGIN_TAP_DETECTOR);
test.AddUserEvent(MakeTouchEvent(m2::PointD::Zero(), df::TouchEvent::TOUCH_DOWN));
test.RunTest();
threads::Sleep(1100);
test.AddExpectation(df::UserEventStream::LONG_TAP_DETECTED);
test.RunTest();
test.AddUserEvent(MakeTouchEvent(m2::PointD::Zero(), df::TouchEvent::TOUCH_UP));
test.RunTest();
}
UNIT_TEST(SimpleDrag)
{
size_t const moveEventCount = 5;
UserEventStreamTest test(false);
test.AddExpectation(df::UserEventStream::TRY_FILTER);
test.AddExpectation(df::UserEventStream::BEGIN_TAP_DETECTOR);
test.AddExpectation(df::UserEventStream::CANCEL_TAP_DETECTOR);
test.AddExpectation(df::UserEventStream::BEGIN_DRAG);
for (size_t i = 0; i < moveEventCount - 2; ++i)
test.AddExpectation(df::UserEventStream::DRAG);
test.AddExpectation(df::UserEventStream::END_DRAG);
m2::PointD pointer = m2::PointD::Zero();
test.AddUserEvent(MakeTouchEvent(pointer, df::TouchEvent::TOUCH_DOWN));
for (size_t i = 0; i < 5; ++i)
{
pointer += m2::PointD(100.0, 0.0);
test.AddUserEvent(MakeTouchEvent(pointer, df::TouchEvent::TOUCH_MOVE));
}
test.AddUserEvent(MakeTouchEvent(pointer, df::TouchEvent::TOUCH_UP));
test.RunTest();
}
UNIT_TEST(SimpleScale)
{
size_t const moveEventCount = 5;
UserEventStreamTest test(false);
test.SetRect(m2::RectD(-250.0, -250.0, 250.0, 250.0));
test.AddExpectation(df::UserEventStream::TWO_FINGERS_TAP);
test.AddExpectation(df::UserEventStream::BEGIN_SCALE);
for (size_t i = 0; i < moveEventCount - 1; ++i)
test.AddExpectation(df::UserEventStream::SCALE);
test.AddExpectation(df::UserEventStream::END_SCALE);
m2::PointD pointer1 = m2::PointD::Zero() + m2::PointD(10.0, 10.0);
m2::PointD pointer2 = m2::PointD::Zero() - m2::PointD(10.0, 10.0);
test.AddUserEvent(MakeTouchEvent(pointer1, pointer2, df::TouchEvent::TOUCH_DOWN));
for (size_t i = 0; i < moveEventCount; ++i)
{
pointer1 += m2::PointD(20.0, 0.0);
pointer2 -= m2::PointD(20.0, 0.0);
test.AddUserEvent(MakeTouchEvent(pointer1, pointer2, df::TouchEvent::TOUCH_MOVE));
}
test.AddUserEvent(MakeTouchEvent(pointer1, pointer2, df::TouchEvent::TOUCH_UP));
test.RunTest();
}
#endif

View file

@ -0,0 +1,11 @@
#pragma once
namespace df
{
struct Hints
{
bool m_isFirstLaunch = false;
bool m_isLaunchByDeepLink = false;
bool m_screenshotMode = false;
};
} // namespace df

View file

@ -0,0 +1,528 @@
#include "drape_frontend/drape_measurer.hpp"
#include "platform/platform.hpp"
#include "geometry/mercator.hpp"
#include <iomanip>
namespace df
{
DrapeMeasurer & DrapeMeasurer::Instance()
{
static DrapeMeasurer s_inst;
return s_inst;
}
void DrapeMeasurer::Start()
{
using namespace std::chrono;
if (m_isEnabled)
return;
m_isEnabled = true;
auto currentTime = steady_clock::now();
#ifdef GENERATING_STATISTIC
m_startScenePreparingTime = currentTime;
m_maxScenePreparingTime = steady_clock::duration::zero();
m_startShapesGenTime = currentTime;
m_totalShapesGenTime = steady_clock::duration::zero();
m_totalShapesCount = 0;
m_startOverlayShapesGenTime = currentTime;
m_totalOverlayShapesGenTime = steady_clock::duration::zero();
m_totalOverlayShapesCount = 0;
#endif
#ifdef TILES_STATISTIC
{
std::lock_guard<std::mutex> lock(m_tilesMutex);
m_tilesReadInfo.clear();
}
#endif
#if defined(RENDER_STATISTIC) || defined(TRACK_GPU_MEM)
m_totalTPF = steady_clock::duration::zero();
m_totalTPFCount = 0;
m_minFPS = std::numeric_limits<uint32_t>::max();
m_fpsDistribution.clear();
m_totalFPS = 0.0;
m_totalFPSCount = 0;
m_startImmediateRenderingTime = currentTime;
m_immediateRenderingFramesCount = 0;
m_immediateRenderingTimeSum = steady_clock::duration::zero();
m_immediateRenderingMinFps = std::numeric_limits<uint32_t>::max();
m_totalFrameRenderTime = steady_clock::duration::zero();
m_totalFramesCount = 0;
#endif
m_startFrameRenderTime = currentTime;
if (m_realtimeTotalFramesCount == 0)
{
m_realtimeMinFrameRenderTime = steady_clock::duration::max();
m_realtimeMaxFrameRenderTime = steady_clock::duration::min();
m_realtimeTotalFrameRenderTime = steady_clock::duration::zero();
m_realtimeSlowFramesCount = 0;
m_realtimeRenderingBox = {};
}
#ifdef TRACK_GPU_MEM
m_maxSnapshotValues = dp::GPUMemTracker::GPUMemorySnapshot();
m_summarySnapshotValues = dp::GPUMemTracker::GPUMemorySnapshot();
m_numberOfSnapshots = 0;
#endif
}
void DrapeMeasurer::Stop(bool forceProcessRealtimeStats /* = false */)
{
using namespace std::chrono;
if (!m_isEnabled)
return;
m_isEnabled = false;
#ifndef DRAPE_MEASURER_BENCHMARK
if ((forceProcessRealtimeStats && m_realtimeTotalFramesCount > 0) || m_realtimeTotalFramesCount >= 1000)
m_realtimeTotalFramesCount = 0;
#endif
}
void DrapeMeasurer::SetApiVersion(dp::ApiVersion apiVersion)
{
m_apiVersion = apiVersion;
}
void DrapeMeasurer::SetResolution(m2::PointU const & resolution)
{
m_resolution = resolution;
}
void DrapeMeasurer::SetGpuName(std::string const & gpuName)
{
m_gpuName = gpuName;
}
#ifdef GENERATING_STATISTIC
void DrapeMeasurer::StartScenePreparing()
{
if (!m_isEnabled)
return;
m_startScenePreparingTime = std::chrono::steady_clock::now();
}
void DrapeMeasurer::EndScenePreparing()
{
if (!m_isEnabled)
return;
m_maxScenePreparingTime = max(m_maxScenePreparingTime, std::chrono::steady_clock::now() - m_startScenePreparingTime);
}
void DrapeMeasurer::StartShapesGeneration()
{
m_startShapesGenTime = std::chrono::steady_clock::now();
}
void DrapeMeasurer::EndShapesGeneration(uint32_t shapesCount)
{
m_totalShapesGenTime += std::chrono::steady_clock::now() - m_startShapesGenTime;
m_totalShapesCount += shapesCount;
}
void DrapeMeasurer::StartOverlayShapesGeneration()
{
m_startOverlayShapesGenTime = std::chrono::steady_clock::now();
}
void DrapeMeasurer::EndOverlayShapesGeneration(uint32_t shapesCount)
{
m_totalOverlayShapesGenTime += std::chrono::steady_clock::now() - m_startOverlayShapesGenTime;
m_totalOverlayShapesCount += shapesCount;
}
std::string DrapeMeasurer::GeneratingStatistic::ToString() const
{
std::ostringstream ss;
ss << " ----- Generation statistic report ----- \n";
ss << " Max scene preparing time, ms = " << m_maxScenePreparingTimeInMs << "\n";
ss << " Shapes total generation time, ms = " << m_shapeGenTimeInMs << "\n";
ss << " Shapes total count = " << m_shapesCount << ", (" << static_cast<double>(m_shapeGenTimeInMs) / m_shapesCount
<< " ms per shape)\n";
ss << " Overlay shapes total generation time, ms = " << m_overlayShapeGenTimeInMs << "\n";
ss << " Overlay shapes total count = " << m_overlayShapesCount << ", ("
<< static_cast<double>(m_overlayShapeGenTimeInMs) / m_overlayShapesCount << " ms per overlay)\n";
ss << " ----- Generation statistic report ----- \n";
return ss.str();
}
DrapeMeasurer::GeneratingStatistic DrapeMeasurer::GetGeneratingStatistic()
{
using namespace std::chrono;
GeneratingStatistic statistic;
statistic.m_shapesCount = m_totalShapesCount;
statistic.m_shapeGenTimeInMs = static_cast<uint32_t>(duration_cast<milliseconds>(m_totalShapesGenTime).count());
statistic.m_overlayShapesCount = m_totalOverlayShapesCount;
statistic.m_overlayShapeGenTimeInMs =
static_cast<uint32_t>(duration_cast<milliseconds>(m_totalOverlayShapesGenTime).count());
statistic.m_maxScenePreparingTimeInMs =
static_cast<uint32_t>(duration_cast<milliseconds>(m_maxScenePreparingTime).count());
return statistic;
}
#endif
#ifdef RENDER_STATISTIC
std::string DrapeMeasurer::RenderStatistic::ToString() const
{
std::ostringstream ss;
ss << " ----- Render statistic report ----- \n";
ss << " FPS = " << m_FPS << "\n";
ss << " Min FPS = " << m_minFPS << "\n";
ss << " Immediate rendering FPS = " << m_immediateRenderingFPS << "\n";
ss << " Immediate rendering min FPS = " << m_immediateRenderingMinFPS << "\n";
ss << " Frame render time, ms = " << m_frameRenderTimeInMs << "\n";
if (!m_fpsDistribution.empty())
{
ss << " FPS Distribution:\n";
for (auto const & fps : m_fpsDistribution)
{
ss << " " << fps.first << "-" << (fps.first + 10) << ": " << std::setprecision(4) << fps.second * 100.0f
<< "%\n";
}
}
ss << " ----- Render statistic report ----- \n";
return ss.str();
}
DrapeMeasurer::RenderStatistic DrapeMeasurer::GetRenderStatistic()
{
using namespace std::chrono;
RenderStatistic statistic;
statistic.m_FPS = static_cast<uint32_t>(m_totalFPS / m_totalFPSCount);
statistic.m_minFPS = m_minFPS;
statistic.m_frameRenderTimeInMs =
static_cast<uint32_t>(duration_cast<milliseconds>(m_totalTPF).count()) / m_totalTPFCount;
uint32_t totalCount = 0;
for (auto const & fps : m_fpsDistribution)
totalCount += fps.second;
if (totalCount != 0)
for (auto const & fps : m_fpsDistribution)
statistic.m_fpsDistribution[fps.first] = static_cast<float>(fps.second) / totalCount;
statistic.m_immediateRenderingMinFPS = m_immediateRenderingMinFps;
if (m_immediateRenderingFramesCount > 0)
{
auto const timeSumMs = duration_cast<milliseconds>(m_immediateRenderingTimeSum).count();
auto const avgFrameTimeMs = static_cast<double>(timeSumMs) / m_immediateRenderingFramesCount;
statistic.m_immediateRenderingFPS = static_cast<uint32_t>(1000.0 / avgFrameTimeMs);
}
return statistic;
}
#endif
void DrapeMeasurer::BeforeRenderFrame()
{
if (!m_isEnabled)
return;
m_startFrameRenderTime = std::chrono::steady_clock::now();
}
void DrapeMeasurer::AfterRenderFrame(bool isActiveFrame, m2::PointD const & viewportCenter)
{
using namespace std::chrono;
if (!m_isEnabled)
return;
auto const frameTime = steady_clock::now() - m_startFrameRenderTime;
if (isActiveFrame)
{
if (mercator::Bounds::FullRect().IsPointInside(viewportCenter))
m_realtimeRenderingBox.Add(viewportCenter);
m_realtimeTotalFrameRenderTime += frameTime;
m_realtimeMinFrameRenderTime = std::min(m_realtimeMinFrameRenderTime, frameTime);
m_realtimeMaxFrameRenderTime = std::max(m_realtimeMaxFrameRenderTime, frameTime);
auto const frameTimeMs = duration_cast<milliseconds>(frameTime).count();
if (frameTimeMs > 30)
++m_realtimeSlowFramesCount;
++m_realtimeTotalFramesCount;
}
#if defined(RENDER_STATISTIC) || defined(TRACK_GPU_MEM)
++m_totalFramesCount;
m_totalFrameRenderTime += frameTime;
auto const elapsed = duration_cast<milliseconds>(m_totalFrameRenderTime).count();
if (elapsed >= 30)
{
double fps = m_totalFramesCount * 1000.0 / elapsed;
m_minFPS = std::min(m_minFPS, static_cast<uint32_t>(fps));
m_totalFPS += fps;
++m_totalFPSCount;
m_totalTPF += m_totalFrameRenderTime / m_totalFramesCount;
++m_totalTPFCount;
auto const fpsGroup = (static_cast<uint32_t>(fps) / 10) * 10;
m_fpsDistribution[fpsGroup]++;
m_totalFramesCount = 0;
m_totalFrameRenderTime = steady_clock::duration::zero();
#ifdef TRACK_GPU_MEM
TakeGPUMemorySnapshot();
#endif
}
#endif
}
#if defined(RENDER_STATISTIC) || defined(TRACK_GPU_MEM)
void DrapeMeasurer::BeforeImmediateRendering()
{
if (!m_isEnabled)
return;
m_startImmediateRenderingTime = std::chrono::steady_clock::now();
}
void DrapeMeasurer::AfterImmediateRendering()
{
using namespace std::chrono;
if (!m_isEnabled)
return;
++m_immediateRenderingFramesCount;
auto const elapsed = steady_clock::now() - m_startImmediateRenderingTime;
m_immediateRenderingTimeSum += elapsed;
auto const elapsedMs = duration_cast<milliseconds>(elapsed).count();
if (elapsedMs > 0)
{
auto const fps = static_cast<uint32_t>(1000 / elapsedMs);
m_immediateRenderingMinFps = std::min(m_immediateRenderingMinFps, fps);
}
}
#endif
#ifdef TILES_STATISTIC
std::string DrapeMeasurer::TileStatistic::ToString() const
{
std::ostringstream ss;
ss << " ----- Tiles read statistic report ----- \n";
ss << " Tile read time, ms = " << m_tileReadTimeInMs << "\n";
ss << " Tiles count = " << m_totalTilesCount << "\n";
ss << " ----- Tiles read statistic report ----- \n";
return ss.str();
}
void DrapeMeasurer::StartTileReading()
{
if (!m_isEnabled)
return;
threads::ThreadID tid = threads::GetCurrentThreadID();
std::shared_ptr<TileReadInfo> tileInfo;
{
std::lock_guard<std::mutex> lock(m_tilesMutex);
auto const it = m_tilesReadInfo.find(tid);
if (it != m_tilesReadInfo.end())
{
tileInfo = it->second;
}
else
{
tileInfo = std::make_shared<TileReadInfo>();
m_tilesReadInfo.insert(make_pair(tid, tileInfo));
}
}
auto const currentTime = std::chrono::steady_clock::now();
tileInfo->m_startTileReadTime = currentTime;
}
void DrapeMeasurer::EndTileReading()
{
if (!m_isEnabled)
return;
auto const currentTime = std::chrono::steady_clock::now();
threads::ThreadID tid = threads::GetCurrentThreadID();
std::shared_ptr<TileReadInfo> tileInfo;
{
std::lock_guard<std::mutex> lock(m_tilesMutex);
auto const it = m_tilesReadInfo.find(tid);
if (it != m_tilesReadInfo.end())
tileInfo = it->second;
else
return;
}
auto passedTime = currentTime - tileInfo->m_startTileReadTime;
tileInfo->m_totalTileReadTime += passedTime;
++tileInfo->m_totalTilesCount;
}
DrapeMeasurer::TileStatistic DrapeMeasurer::GetTileStatistic()
{
using namespace std::chrono;
TileStatistic statistic;
{
std::lock_guard<std::mutex> lock(m_tilesMutex);
for (auto const & it : m_tilesReadInfo)
{
statistic.m_tileReadTimeInMs +=
static_cast<uint32_t>(duration_cast<milliseconds>(it.second->m_totalTileReadTime).count());
statistic.m_totalTilesCount += it.second->m_totalTilesCount;
}
}
if (statistic.m_totalTilesCount > 0)
statistic.m_tileReadTimeInMs /= statistic.m_totalTilesCount;
return statistic;
}
#endif
#ifdef TRACK_GPU_MEM
std::string DrapeMeasurer::GPUMemoryStatistic::ToString() const
{
std::ostringstream ss;
ss << " ----- GPU memory report ----- \n";
ss << " --Max memory values:\n";
ss << m_maxMemoryValues.ToString();
ss << "\n --Average memory values:\n";
ss << m_averageMemoryValues.ToString();
ss << " ----- GPU memory report ----- \n";
return ss.str();
}
void DrapeMeasurer::TakeGPUMemorySnapshot()
{
dp::GPUMemTracker::GPUMemorySnapshot snap = dp::GPUMemTracker::Inst().GetMemorySnapshot();
for (auto const & tagPair : snap.m_tagStats)
{
auto itMax = m_maxSnapshotValues.m_tagStats.find(tagPair.first);
if (itMax != m_maxSnapshotValues.m_tagStats.end())
{
itMax->second.m_objectsCount = std::max(itMax->second.m_objectsCount, tagPair.second.m_objectsCount);
itMax->second.m_alocatedInMb = std::max(itMax->second.m_alocatedInMb, tagPair.second.m_alocatedInMb);
itMax->second.m_usedInMb = std::max(itMax->second.m_alocatedInMb, tagPair.second.m_usedInMb);
}
else
{
m_maxSnapshotValues.m_tagStats.insert(tagPair);
}
auto itSummary = m_summarySnapshotValues.m_tagStats.find(tagPair.first);
if (itSummary != m_summarySnapshotValues.m_tagStats.end())
{
itSummary->second.m_objectsCount += tagPair.second.m_objectsCount;
itSummary->second.m_alocatedInMb += tagPair.second.m_alocatedInMb;
itSummary->second.m_usedInMb += tagPair.second.m_usedInMb;
}
else
{
m_summarySnapshotValues.m_tagStats.insert(tagPair);
}
}
m_maxSnapshotValues.m_summaryAllocatedInMb =
std::max(snap.m_summaryAllocatedInMb, m_maxSnapshotValues.m_summaryAllocatedInMb);
m_maxSnapshotValues.m_summaryUsedInMb = std::max(snap.m_summaryUsedInMb, m_maxSnapshotValues.m_summaryUsedInMb);
m_summarySnapshotValues.m_summaryAllocatedInMb += snap.m_summaryAllocatedInMb;
m_summarySnapshotValues.m_summaryUsedInMb += snap.m_summaryUsedInMb;
++m_numberOfSnapshots;
}
DrapeMeasurer::GPUMemoryStatistic DrapeMeasurer::GetGPUMemoryStatistic()
{
GPUMemoryStatistic statistic;
statistic.m_maxMemoryValues = m_maxSnapshotValues;
statistic.m_averageMemoryValues = m_summarySnapshotValues;
if (m_numberOfSnapshots > 0)
{
for (auto & tagPair : statistic.m_averageMemoryValues.m_tagStats)
{
tagPair.second.m_objectsCount /= m_numberOfSnapshots;
tagPair.second.m_alocatedInMb /= m_numberOfSnapshots;
tagPair.second.m_usedInMb /= m_numberOfSnapshots;
}
statistic.m_averageMemoryValues.m_summaryAllocatedInMb /= m_numberOfSnapshots;
statistic.m_averageMemoryValues.m_summaryUsedInMb /= m_numberOfSnapshots;
}
return statistic;
}
#endif
std::string DrapeMeasurer::DrapeStatistic::ToString() const
{
std::ostringstream ss;
ss << "\n ===== Drape statistic report ===== \n";
#ifdef RENDER_STATISTIC
ss << "\n" << m_renderStatistic.ToString() << "\n";
#endif
#ifdef TILES_STATISTIC
ss << "\n" << m_tileStatistic.ToString() << "\n";
#endif
#ifdef GENERATING_STATISTIC
ss << "\n" << m_generatingStatistic.ToString() << "\n";
#endif
#ifdef TRACK_GPU_MEM
ss << "\n" << m_gpuMemStatistic.ToString() << "\n";
#endif
#ifdef TRACK_GLYPH_USAGE
ss << "\n" << m_glyphStatistic.ToString() << "\n";
#endif
ss << "\n ===== Drape statistic report ===== \n\n";
return ss.str();
}
DrapeMeasurer::DrapeStatistic DrapeMeasurer::GetDrapeStatistic()
{
DrapeStatistic statistic;
#ifdef RENDER_STATISTIC
statistic.m_renderStatistic = GetRenderStatistic();
#endif
#ifdef TILES_STATISTIC
statistic.m_tileStatistic = GetTileStatistic();
#endif
#ifdef GENERATING_STATISTIC
statistic.m_generatingStatistic = GetGeneratingStatistic();
#endif
#ifdef TRACK_GPU_MEM
statistic.m_gpuMemStatistic = GetGPUMemoryStatistic();
#endif
#ifdef TRACK_GLYPH_USAGE
statistic.m_glyphStatistic = dp::GlyphUsageTracker::Instance().Report();
#endif
return statistic;
}
} // namespace df

View file

@ -0,0 +1,202 @@
#pragma once
#include "drape/drape_diagnostics.hpp"
#include "drape/utils/glyph_usage_tracker.hpp"
#include "drape/utils/gpu_mem_tracker.hpp"
#include "geometry/rect2d.hpp"
#include "base/thread.hpp"
#include "base/timer.hpp"
#include <chrono>
#include <drape/drape_global.hpp>
#include <map>
#include <memory>
#include <mutex>
#include <numeric>
#include <unordered_map>
#include <vector>
namespace df
{
class DrapeMeasurer
{
public:
static DrapeMeasurer & Instance();
void Start();
void Stop(bool forceProcessRealtimeStats = false);
void SetGpuName(std::string const & gpuName);
void SetApiVersion(dp::ApiVersion apiVersion);
void SetResolution(m2::PointU const & resolution);
#ifdef RENDER_STATISTIC
struct RenderStatistic
{
std::string ToString() const;
uint32_t m_FPS = 0;
uint32_t m_minFPS = 0;
uint32_t m_frameRenderTimeInMs = 0;
std::map<uint32_t, float> m_fpsDistribution;
uint32_t m_immediateRenderingFPS = 0;
uint32_t m_immediateRenderingMinFPS = 0;
};
RenderStatistic GetRenderStatistic();
#endif
#ifdef TILES_STATISTIC
struct TileStatistic
{
std::string ToString() const;
uint32_t m_totalTilesCount = 0;
uint32_t m_tileReadTimeInMs = 0;
};
void StartTileReading();
void EndTileReading();
TileStatistic GetTileStatistic();
#endif
#ifdef GENERATING_STATISTIC
struct GeneratingStatistic
{
std::string ToString() const;
uint32_t m_maxScenePreparingTimeInMs = 0;
uint32_t m_shapesCount = 0;
uint32_t m_shapeGenTimeInMs = 0;
uint32_t m_overlayShapesCount = 0;
uint32_t m_overlayShapeGenTimeInMs = 0;
};
void StartScenePreparing();
void EndScenePreparing();
void StartShapesGeneration();
void EndShapesGeneration(uint32_t shapesCount);
void StartOverlayShapesGeneration();
void EndOverlayShapesGeneration(uint32_t shapesCount);
GeneratingStatistic GetGeneratingStatistic();
#endif
#ifdef TRACK_GPU_MEM
struct GPUMemoryStatistic
{
std::string ToString() const;
dp::GPUMemTracker::GPUMemorySnapshot m_averageMemoryValues;
dp::GPUMemTracker::GPUMemorySnapshot m_maxMemoryValues;
};
GPUMemoryStatistic GetGPUMemoryStatistic();
#endif
void BeforeRenderFrame();
void AfterRenderFrame(bool isActiveFrame, m2::PointD const & viewportCenter);
#if defined(RENDER_STATISTIC) || defined(TRACK_GPU_MEM)
void BeforeImmediateRendering();
void AfterImmediateRendering();
#endif
struct DrapeStatistic
{
std::string ToString() const;
#ifdef RENDER_STATISTIC
RenderStatistic m_renderStatistic;
#endif
#ifdef TILES_STATISTIC
TileStatistic m_tileStatistic;
#endif
#ifdef GENERATING_STATISTIC
GeneratingStatistic m_generatingStatistic;
#endif
#ifdef TRACK_GPU_MEM
GPUMemoryStatistic m_gpuMemStatistic;
#endif
#ifdef TRACK_GLYPH_USAGE
dp::GlyphUsageTracker::GlyphUsageStatistic m_glyphStatistic;
#endif
};
DrapeStatistic GetDrapeStatistic();
private:
DrapeMeasurer() = default;
dp::ApiVersion m_apiVersion = dp::ApiVersion::Invalid;
m2::PointU m_resolution;
std::string m_gpuName;
bool m_isEnabled = false;
#ifdef GENERATING_STATISTIC
std::chrono::time_point<std::chrono::steady_clock> m_startScenePreparingTime;
std::chrono::nanoseconds m_maxScenePreparingTime;
std::chrono::time_point<std::chrono::steady_clock> m_startShapesGenTime;
std::chrono::nanoseconds m_totalShapesGenTime;
uint32_t m_totalShapesCount = 0;
std::chrono::time_point<std::chrono::steady_clock> m_startOverlayShapesGenTime;
std::chrono::nanoseconds m_totalOverlayShapesGenTime;
uint32_t m_totalOverlayShapesCount = 0;
#endif
#ifdef TILES_STATISTIC
struct TileReadInfo
{
std::chrono::time_point<std::chrono::steady_clock> m_startTileReadTime;
std::chrono::nanoseconds m_totalTileReadTime;
uint32_t m_totalTilesCount = 0;
};
std::map<threads::ThreadID, std::shared_ptr<TileReadInfo>> m_tilesReadInfo;
std::mutex m_tilesMutex;
#endif
std::chrono::time_point<std::chrono::steady_clock> m_startFrameRenderTime;
std::chrono::nanoseconds m_realtimeMinFrameRenderTime;
std::chrono::nanoseconds m_realtimeMaxFrameRenderTime;
std::chrono::nanoseconds m_realtimeTotalFrameRenderTime;
uint64_t m_realtimeTotalFramesCount = 0;
uint64_t m_realtimeSlowFramesCount = 0;
m2::RectD m_realtimeRenderingBox;
#if defined(RENDER_STATISTIC) || defined(TRACK_GPU_MEM)
std::chrono::nanoseconds m_totalFrameRenderTime;
uint32_t m_totalFramesCount = 0;
std::chrono::nanoseconds m_totalTPF;
uint32_t m_totalTPFCount = 0;
uint32_t m_minFPS = std::numeric_limits<uint32_t>::max();
double m_totalFPS = 0.0;
uint32_t m_totalFPSCount = 0;
std::unordered_map<uint32_t, uint32_t> m_fpsDistribution;
std::chrono::time_point<std::chrono::steady_clock> m_startImmediateRenderingTime;
uint32_t m_immediateRenderingMinFps = std::numeric_limits<uint32_t>::max();
std::chrono::nanoseconds m_immediateRenderingTimeSum;
uint64_t m_immediateRenderingFramesCount = 0;
#endif
#ifdef TRACK_GPU_MEM
void TakeGPUMemorySnapshot();
dp::GPUMemTracker::GPUMemorySnapshot m_maxSnapshotValues;
dp::GPUMemTracker::GPUMemorySnapshot m_summarySnapshotValues;
uint32_t m_numberOfSnapshots = 0;
#endif
};
} // namespace df

View file

@ -0,0 +1,34 @@
#include "drape_frontend/drape_notifier.hpp"
#include "drape_frontend/message_subclasses.hpp"
#include "drape/drape_routine.hpp"
namespace df
{
DrapeNotifier::DrapeNotifier(ref_ptr<ThreadsCommutator> commutator)
: m_commutator(commutator)
, m_counter(kInvalidId + 1)
{}
uint64_t DrapeNotifier::Notify(ThreadsCommutator::ThreadName threadName,
base::DelayedThreadPool::Duration const & duration, bool repeating, Functor && functor)
{
uint64_t const notifyId = m_counter++;
NotifyImpl(threadName, duration, repeating, notifyId, std::move(functor));
return notifyId;
}
void DrapeNotifier::NotifyImpl(ThreadsCommutator::ThreadName threadName,
base::DelayedThreadPool::Duration const & duration, bool repeating, uint64_t notifyId,
Functor && functor)
{
dp::DrapeRoutine::RunDelayed(duration,
[this, threadName, duration, repeating, notifyId, functor = std::move(functor)]() mutable
{
m_commutator->PostMessage(threadName, make_unique_dp<NotifyRenderThreadMessage>(functor, notifyId),
MessagePriority::Normal);
if (repeating)
NotifyImpl(threadName, duration, repeating, notifyId, std::move(functor));
});
}
} // namespace df

View file

@ -0,0 +1,33 @@
#pragma once
#include "drape_frontend/threads_commutator.hpp"
#include "drape/pointers.hpp"
#include "base/thread_pool_delayed.hpp"
#include <atomic>
#include <cstdint>
#include <functional>
namespace df
{
class DrapeNotifier
{
public:
static uint64_t constexpr kInvalidId = 0;
using Functor = std::function<void(uint64_t notifyId)>;
explicit DrapeNotifier(ref_ptr<ThreadsCommutator> commutator);
uint64_t Notify(ThreadsCommutator::ThreadName threadName, base::DelayedThreadPool::Duration const & duration,
bool repeating, Functor && functor);
private:
void NotifyImpl(ThreadsCommutator::ThreadName threadName, base::DelayedThreadPool::Duration const & duration,
bool repeating, uint64_t notifyId, Functor && functor);
ref_ptr<ThreadsCommutator> m_commutator;
std::atomic<uint64_t> m_counter;
};
} // namespace df

View file

@ -0,0 +1,66 @@
#include "drape_frontend/engine_context.hpp"
#include "drape/texture_manager.hpp"
#include "drape_frontend/message_subclasses.hpp"
#include <utility>
namespace df
{
EngineContext::EngineContext(TileKey tileKey, ref_ptr<ThreadsCommutator> commutator, ref_ptr<dp::TextureManager> texMng,
ref_ptr<MetalineManager> metalineMng, CustomFeaturesContextWeakPtr customFeaturesContext,
bool is3dBuildingsEnabled, bool isTrafficEnabled, bool isolinesEnabled,
int8_t mapLangIndex)
: m_tileKey(tileKey)
, m_commutator(commutator)
, m_texMng(texMng)
, m_metalineMng(metalineMng)
, m_customFeaturesContext(customFeaturesContext)
, m_3dBuildingsEnabled(is3dBuildingsEnabled)
, m_trafficEnabled(isTrafficEnabled)
, m_isolinesEnabled(isolinesEnabled)
, m_mapLangIndex(mapLangIndex)
{}
ref_ptr<dp::TextureManager> EngineContext::GetTextureManager() const
{
return m_texMng;
}
ref_ptr<MetalineManager> EngineContext::GetMetalineManager() const
{
return m_metalineMng;
}
void EngineContext::BeginReadTile()
{
PostMessage(make_unique_dp<TileReadStartMessage>(m_tileKey));
}
void EngineContext::Flush(TMapShapes && shapes)
{
PostMessage(make_unique_dp<MapShapeReadedMessage>(m_tileKey, std::move(shapes)));
}
void EngineContext::FlushOverlays(TMapShapes && shapes)
{
PostMessage(make_unique_dp<OverlayMapShapeReadedMessage>(m_tileKey, std::move(shapes)));
}
void EngineContext::FlushTrafficGeometry(TrafficSegmentsGeometry && geometry)
{
m_commutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
make_unique_dp<FlushTrafficGeometryMessage>(m_tileKey, std::move(geometry)),
MessagePriority::Low);
}
void EngineContext::EndReadTile()
{
PostMessage(make_unique_dp<TileReadEndMessage>(m_tileKey));
}
void EngineContext::PostMessage(drape_ptr<Message> && message)
{
m_commutator->PostMessage(ThreadsCommutator::ResourceUploadThread, std::move(message), MessagePriority::Normal);
}
} // namespace df

View file

@ -0,0 +1,59 @@
#pragma once
#include "drape_frontend/custom_features_context.hpp"
#include "drape_frontend/map_shape.hpp"
#include "drape_frontend/threads_commutator.hpp"
#include "drape_frontend/tile_utils.hpp"
#include "drape_frontend/traffic_generator.hpp"
#include "drape/constants.hpp"
#include "drape/pointers.hpp"
#include <functional>
namespace dp
{
class TextureManager;
} // namespace dp
namespace df
{
class Message;
class MetalineManager;
class EngineContext
{
public:
EngineContext(TileKey tileKey, ref_ptr<ThreadsCommutator> commutator, ref_ptr<dp::TextureManager> texMng,
ref_ptr<MetalineManager> metalineMng, CustomFeaturesContextWeakPtr customFeaturesContext,
bool is3dBuildingsEnabled, bool isTrafficEnabled, bool isolinesEnabled, int8_t mapLangIndex);
TileKey const & GetTileKey() const { return m_tileKey; }
bool Is3dBuildingsEnabled() const { return m_3dBuildingsEnabled; }
bool IsTrafficEnabled() const { return m_trafficEnabled; }
bool IsolinesEnabled() const { return m_isolinesEnabled; }
int8_t GetMapLangIndex() const { return m_mapLangIndex; }
CustomFeaturesContextWeakPtr GetCustomFeaturesContext() const { return m_customFeaturesContext; }
ref_ptr<dp::TextureManager> GetTextureManager() const;
ref_ptr<MetalineManager> GetMetalineManager() const;
void BeginReadTile();
void Flush(TMapShapes && shapes);
void FlushOverlays(TMapShapes && shapes);
void FlushTrafficGeometry(TrafficSegmentsGeometry && geometry);
void EndReadTile();
private:
void PostMessage(drape_ptr<Message> && message);
TileKey m_tileKey;
ref_ptr<ThreadsCommutator> m_commutator;
ref_ptr<dp::TextureManager> m_texMng;
ref_ptr<MetalineManager> m_metalineMng;
CustomFeaturesContextWeakPtr m_customFeaturesContext;
bool m_3dBuildingsEnabled;
bool m_trafficEnabled;
bool m_isolinesEnabled;
int8_t m_mapLangIndex;
};
} // namespace df

View file

@ -0,0 +1,52 @@
#pragma once
#include "drape/glsl_types.hpp"
#include "base/assert.hpp"
#include <type_traits>
#include <utility>
namespace df
{
#define DECLARE_SETTER(name, field) \
template <typename T> \
struct Check##name \
{ \
private: \
static void Detect(...); \
template <typename U> \
static decltype(std::declval<U>().field) Detect(U const &); \
\
public: \
static constexpr bool Value = !std::is_same<void, decltype(Detect(std::declval<T>()))>::value; \
}; \
template <typename ParamsType> \
std::enable_if_t<Check##name<ParamsType>::Value> name(ParamsType & params) const \
{ \
params.field = field; \
} \
template <typename ParamsType> \
std::enable_if_t<!Check##name<ParamsType>::Value> name(ParamsType & params) const \
{}
struct FrameValues
{
glsl::mat4 m_projection;
glsl::mat4 m_pivotTransform;
float m_zScale = 1.0f;
template <typename ParamsType>
void SetTo(ParamsType & params) const
{
SetProjection(params);
SetPivotTransform(params);
SetZScale(params);
}
private:
DECLARE_SETTER(SetProjection, m_projection)
DECLARE_SETTER(SetPivotTransform, m_pivotTransform)
DECLARE_SETTER(SetZScale, m_zScale)
};
} // namespace df

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,452 @@
#pragma once
#include "drape_frontend/base_renderer.hpp"
#include "drape_frontend/drape_api_renderer.hpp"
#include "drape_frontend/frame_values.hpp"
#include "drape_frontend/gps_track_renderer.hpp"
#include "drape_frontend/gui/layer_render.hpp"
#include "drape_frontend/my_position_controller.hpp"
#include "drape_frontend/overlays_tracker.hpp"
#include "drape_frontend/postprocess_renderer.hpp"
#include "drape_frontend/render_group.hpp"
#include "drape_frontend/render_state_extension.hpp"
#include "drape_frontend/requested_tiles.hpp"
#include "drape_frontend/route_renderer.hpp"
#include "drape_frontend/threads_commutator.hpp"
#include "drape_frontend/traffic_renderer.hpp"
#include "drape_frontend/transit_scheme_renderer.hpp"
#include "drape_frontend/user_event_stream.hpp"
#include "kml/type_utils.hpp"
#include "shaders/program_manager.hpp"
#include "drape/overlay_tree.hpp"
#include "drape/pointers.hpp"
#include "platform/location.hpp"
#include "geometry/screenbase.hpp"
#include "geometry/triangle2d.hpp"
#include "base/thread.hpp"
#include <array>
#include <functional>
#include <memory>
#include <optional>
#include <vector>
namespace dp
{
class Framebuffer;
class OverlayTree;
class RenderBucket;
} // namespace dp
namespace df
{
class DebugRectRenderer;
class DrapeNotifier;
class ScenarioManager;
class ScreenQuadRenderer;
class SelectionShape;
class SelectObjectMessage;
struct TapInfo
{
m2::PointD const m_mercator;
bool const m_isLong;
bool const m_isMyPositionTapped;
FeatureID const m_featureTapped;
kml::MarkId const m_markTapped;
static m2::AnyRectD GetDefaultTapRect(m2::PointD const & mercator, ScreenBase const & screen);
static m2::AnyRectD GetBookmarkTapRect(m2::PointD const & mercator, ScreenBase const & screen);
static m2::AnyRectD GetRoutingPointTapRect(m2::PointD const & mercator, ScreenBase const & screen);
static m2::AnyRectD GetGuideTapRect(m2::PointD const & mercator, ScreenBase const & screen);
static m2::AnyRectD GetPreciseTapRect(m2::PointD const & mercator, double eps);
};
/*
* A FrontendRenderer holds several RenderLayers, one per each df::DepthLayer,
* a rendering order of the layers is set in RenderScene().
* Each RenderLayer contains several RenderGroups, one per each tile and RenderState.
* Each RenderGroup contains several RenderBuckets holding VertexArrayBuffers and optional OverlayHandles.
*/
class FrontendRenderer
: public BaseRenderer
, public MyPositionController::Listener
, public UserEventStream::Listener
{
public:
using ModelViewChangedHandler = std::function<void(ScreenBase const & screen)>;
using GraphicsReadyHandler = std::function<void()>;
using TapEventInfoHandler = std::function<void(TapInfo const &)>;
using UserPositionChangedHandler = std::function<void(m2::PointD const & pt, bool hasPosition)>;
struct Params : BaseRenderer::Params
{
Params(dp::ApiVersion apiVersion, ref_ptr<ThreadsCommutator> commutator,
ref_ptr<dp::GraphicsContextFactory> factory, ref_ptr<dp::TextureManager> texMng,
MyPositionController::Params && myPositionParams, dp::Viewport viewport,
ModelViewChangedHandler && modelViewChangedHandler, TapEventInfoHandler && tapEventHandler,
UserPositionChangedHandler && positionChangedHandler, ref_ptr<RequestedTiles> requestedTiles,
OverlaysShowStatsCallback && overlaysShowStatsCallback, bool allow3dBuildings, bool trafficEnabled,
bool blockTapEvents, std::vector<PostprocessRenderer::Effect> && enabledEffects,
OnGraphicsContextInitialized const & onGraphicsContextInitialized,
dp::RenderInjectionHandler && renderInjectionHandler)
: BaseRenderer::Params(apiVersion, commutator, factory, texMng, onGraphicsContextInitialized)
, m_myPositionParams(std::move(myPositionParams))
, m_viewport(viewport)
, m_modelViewChangedHandler(std::move(modelViewChangedHandler))
, m_tapEventHandler(std::move(tapEventHandler))
, m_positionChangedHandler(std::move(positionChangedHandler))
, m_requestedTiles(requestedTiles)
, m_overlaysShowStatsCallback(std::move(overlaysShowStatsCallback))
, m_allow3dBuildings(allow3dBuildings)
, m_trafficEnabled(trafficEnabled)
, m_blockTapEvents(blockTapEvents)
, m_enabledEffects(std::move(enabledEffects))
, m_renderInjectionHandler(std::move(renderInjectionHandler))
{}
MyPositionController::Params m_myPositionParams;
dp::Viewport m_viewport;
ModelViewChangedHandler m_modelViewChangedHandler;
TapEventInfoHandler m_tapEventHandler;
UserPositionChangedHandler m_positionChangedHandler;
ref_ptr<RequestedTiles> m_requestedTiles;
OverlaysShowStatsCallback m_overlaysShowStatsCallback;
bool m_allow3dBuildings;
bool m_trafficEnabled;
bool m_blockTapEvents;
std::vector<PostprocessRenderer::Effect> m_enabledEffects;
dp::RenderInjectionHandler m_renderInjectionHandler;
};
explicit FrontendRenderer(Params && params);
~FrontendRenderer() override;
void Teardown();
void AddUserEvent(drape_ptr<UserEvent> && event);
// MyPositionController::Listener
void PositionChanged(m2::PointD const & position, bool hasPosition) override;
void ChangeModelView(m2::PointD const & center, int zoomLevel,
TAnimationCreator const & parallelAnimCreator) override;
void ChangeModelView(double azimuth, TAnimationCreator const & parallelAnimCreator) override;
void ChangeModelView(m2::RectD const & rect, TAnimationCreator const & parallelAnimCreator) override;
void ChangeModelView(m2::PointD const & userPos, double azimuth, m2::PointD const & pxZero, int preferredZoomLevel,
Animation::TAction const & onFinishAction,
TAnimationCreator const & parallelAnimCreator) override;
void ChangeModelView(double autoScale, m2::PointD const & userPos, double azimuth, m2::PointD const & pxZero,
TAnimationCreator const & parallelAnimCreator) override;
drape_ptr<ScenarioManager> const & GetScenarioManager() const { return m_scenarioManager; }
location::EMyPositionMode GetMyPositionMode() const { return m_myPositionController->GetCurrentMode(); }
void OnEnterBackground();
protected:
void AcceptMessage(ref_ptr<Message> message) override;
std::unique_ptr<threads::IRoutine> CreateRoutine() override;
void RenderFrame() override;
void OnContextCreate() override;
void OnContextDestroy() override;
void OnRenderingEnabled() override;
void OnRenderingDisabled() override;
private:
void OnResize(ScreenBase const & screen);
void RenderScene(ScreenBase const & modelView, bool activeFrame);
void PrepareBucket(dp::RenderState const & state, drape_ptr<dp::RenderBucket> & bucket);
void RenderSingleGroup(ref_ptr<dp::GraphicsContext> context, ScreenBase const & modelView,
ref_ptr<BaseRenderGroup> group);
void RefreshProjection(ScreenBase const & screen);
void RefreshZScale(ScreenBase const & screen);
void RefreshPivotTransform(ScreenBase const & screen);
void RefreshBgColor();
struct RenderLayer
{
std::vector<drape_ptr<RenderGroup>> m_renderGroups;
bool m_isDirty = false;
void Sort(ref_ptr<dp::OverlayTree> overlayTree);
};
// Render part of scene
void Render2dLayer(ScreenBase const & modelView);
void PreRender3dLayer(ScreenBase const & modelView);
void Render3dLayer(ScreenBase const & modelView);
void RenderOverlayLayer(ScreenBase const & modelView);
void RenderOverlayUnderBuildingLayer(ScreenBase const & modelView);
void RenderUserMarksLayer(ScreenBase const & modelView, DepthLayer layerId);
void RenderNonDisplaceableUserMarksLayer(ScreenBase const & modelView, DepthLayer layerId);
void RenderTransitSchemeLayer(ScreenBase const & modelView);
void RenderTrafficLayer(ScreenBase const & modelView);
void RenderRouteLayer(ScreenBase const & modelView);
void RenderTransitBackground();
void RenderEmptyFrame();
bool HasTransitRouteData() const;
bool HasRouteData() const;
ScreenBase const & ProcessEvents(bool & modelViewChanged, bool & viewportChanged, bool & needActiveFrame);
void PrepareScene(ScreenBase const & modelView);
void UpdateScene(ScreenBase const & modelView);
void BuildOverlayTree(ScreenBase const & modelView);
void EmitModelViewChanged(ScreenBase const & modelView) const;
#if defined(OMIM_OS_DESKTOP)
void EmitGraphicsReady();
#endif
TTilesCollection ResolveTileKeys(ScreenBase const & screen);
void ResolveZoomLevel(ScreenBase const & screen);
void UpdateDisplacementEnabled();
void CheckIsometryMinScale(ScreenBase const & screen);
void DisablePerspective();
void OnTap(m2::PointD const & pt, bool isLong) override;
void OnForceTap(m2::PointD const & pt) override;
void OnDoubleTap(m2::PointD const & pt) override;
void OnTwoFingersTap() override;
bool OnSingleTouchFiltrate(m2::PointD const & pt, TouchEvent::ETouchType type) override;
void OnDragStarted() override;
void OnDragEnded(m2::PointD const & distance) override;
void OnScaleStarted() override;
void OnRotated() override;
void OnScrolled(m2::PointD const & distance) override;
void CorrectScalePoint(m2::PointD & pt) const override;
void CorrectScalePoint(m2::PointD & pt1, m2::PointD & pt2) const override;
void CorrectGlobalScalePoint(m2::PointD & pt) const override;
void OnScaleEnded() override;
void OnAnimatedScaleEnded() override;
void OnTouchMapAction(TouchEvent::ETouchType touchType, bool isMapTouch) override;
bool OnNewVisibleViewport(m2::RectD const & oldViewport, m2::RectD const & newViewport, bool needOffset,
m2::PointD & gOffset) override;
class Routine : public threads::IRoutine
{
public:
Routine(FrontendRenderer & renderer);
void Do() override;
private:
FrontendRenderer & m_renderer;
};
void ReleaseResources();
void UpdateContextDependentResources();
void BeginUpdateOverlayTree(ScreenBase const & modelView);
void UpdateOverlayTree(ScreenBase const & modelView, drape_ptr<RenderGroup> & renderGroup);
void EndUpdateOverlayTree();
template <typename TRenderGroup>
void AddToRenderGroup(dp::RenderState const & state, drape_ptr<dp::RenderBucket> && renderBucket,
TileKey const & newTile);
using TRenderGroupRemovePredicate = std::function<bool(drape_ptr<RenderGroup> const &)>;
void RemoveRenderGroupsLater(TRenderGroupRemovePredicate const & predicate);
void FollowRoute(int preferredZoomLevel, int preferredZoomLevelIn3d, bool enableAutoZoom, bool isArrowGlued);
bool CheckRouteRecaching(ref_ptr<BaseSubrouteData> subrouteData);
void InvalidateRect(m2::RectD const & gRect);
bool CheckTileGenerations(TileKey const & tileKey);
void UpdateCanBeDeletedStatus();
void OnCompassTapped();
std::pair<FeatureID, kml::MarkId> GetVisiblePOI(m2::PointD const & pixelPoint);
std::pair<FeatureID, kml::MarkId> GetVisiblePOI(m2::RectD const & pixelRect);
void PullToBoundArea(bool randomPlace, bool applyZoom);
void ProcessSelection(ref_ptr<SelectObjectMessage> msg);
void OnPrepareRouteArrows(dp::DrapeID subrouteIndex, std::vector<ArrowBorders> && borders);
void OnCacheRouteArrows(dp::DrapeID subrouteIndex, std::vector<ArrowBorders> const & borders);
void CollectShowOverlaysEvents();
void CheckAndRunFirstLaunchAnimation();
void ScheduleOverlayCollecting();
void SearchInNonDisplaceableUserMarksLayer(ScreenBase const & modelView, DepthLayer layerId,
m2::RectD const & selectionRect, dp::TOverlayContainer & result);
drape_ptr<gpu::ProgramManager> m_gpuProgramManager;
std::array<RenderLayer, static_cast<size_t>(DepthLayer::LayersCount)> m_layers;
drape_ptr<gui::LayerRenderer> m_guiRenderer;
gui::TWidgetsLayoutInfo m_lastWidgetsLayout;
drape_ptr<MyPositionController> m_myPositionController;
drape_ptr<SelectionShape> m_selectionShape;
struct SelectionTrackInfo
{
SelectionTrackInfo() = default;
SelectionTrackInfo(m2::AnyRectD const & startRect, m2::PointD const & startPos)
: m_startRect(startRect)
, m_startPos(startPos)
{}
m2::AnyRectD m_startRect;
m2::PointD m_startPos;
m2::PointI m_snapSides = m2::PointI::Zero();
};
std::optional<SelectionTrackInfo> m_selectionTrackInfo;
drape_ptr<RouteRenderer> m_routeRenderer;
drape_ptr<TrafficRenderer> m_trafficRenderer;
drape_ptr<TransitSchemeRenderer> m_transitSchemeRenderer;
drape_ptr<dp::Framebuffer> m_buildingsFramebuffer;
drape_ptr<ScreenQuadRenderer> m_screenQuadRenderer;
drape_ptr<GpsTrackRenderer> m_gpsTrackRenderer;
drape_ptr<DrapeApiRenderer> m_drapeApiRenderer;
drape_ptr<dp::OverlayTree> m_overlayTree;
FrameValues m_frameValues;
bool m_enablePerspectiveInNavigation;
bool m_enable3dBuildings;
bool m_isIsometry;
bool m_blockTapEvents;
bool m_choosePositionMode;
bool m_screenshotMode;
int8_t m_mapLangIndex;
dp::Viewport m_viewport;
UserEventStream m_userEventStream;
ModelViewChangedHandler m_modelViewChangedHandler;
TapEventInfoHandler m_tapEventInfoHandler;
UserPositionChangedHandler m_userPositionChangedHandler;
ScreenBase m_lastReadedModelView;
TTilesCollection m_notFinishedTiles;
bool IsValidCurrentZoom() const
{
/// @todo Well, this function was introduced to ASSERT m_currentZoomLevel != -1.
/// Can't say for sure is it right or wrong, but also can't garantee with post-messages order.
// In some cases RenderScene, UpdateContextDependentResources can be called before the rendering of
// the first frame. m_currentZoomLevel will be equal to -1, before ResolveZoomLevel call.
return m_currentZoomLevel >= 0;
}
int GetCurrentZoom() const
{
ASSERT(IsValidCurrentZoom(), ());
return m_currentZoomLevel;
}
int m_currentZoomLevel = -1;
ref_ptr<RequestedTiles> m_requestedTiles;
uint64_t m_maxGeneration;
uint64_t m_maxUserMarksGeneration;
int m_lastRecacheRouteId = 0;
struct FollowRouteData
{
FollowRouteData(int preferredZoomLevel, int preferredZoomLevelIn3d, bool enableAutoZoom, bool isArrowGlued)
: m_preferredZoomLevel(preferredZoomLevel)
, m_preferredZoomLevelIn3d(preferredZoomLevelIn3d)
, m_enableAutoZoom(enableAutoZoom)
, m_isArrowGlued(isArrowGlued)
{}
int m_preferredZoomLevel;
int m_preferredZoomLevelIn3d;
bool m_enableAutoZoom;
bool m_isArrowGlued;
};
std::unique_ptr<FollowRouteData> m_pendingFollowRoute;
std::vector<m2::TriangleD> m_dragBoundArea;
drape_ptr<SelectObjectMessage> m_selectObjectMessage;
bool m_needRestoreSize;
bool m_trafficEnabled;
bool m_transitSchemeEnabled = false;
drape_ptr<OverlaysTracker> m_overlaysTracker;
OverlaysShowStatsCallback m_overlaysShowStatsCallback;
bool m_forceUpdateScene;
bool m_forceUpdateUserMarks;
drape_ptr<PostprocessRenderer> m_postprocessRenderer;
std::vector<PostprocessRenderer::Effect> m_enabledOnStartEffects;
bool m_isDebugRectRenderingEnabled = false;
drape_ptr<DebugRectRenderer> m_debugRectRenderer;
drape_ptr<ScenarioManager> m_scenarioManager;
bool m_firstTilesReady = false;
bool m_firstLaunchAnimationTriggered = false;
bool m_firstLaunchAnimationInterrupted = false;
#if defined(OMIM_OS_DESKTOP)
GraphicsReadyHandler m_graphicsReadyFn;
enum class GraphicsStage
{
Unknown,
WaitReady,
WaitRendering,
Rendered
};
GraphicsStage m_graphicsStage = GraphicsStage::Unknown;
#endif
bool m_finishTexturesInitialization = false;
drape_ptr<ScreenQuadRenderer> m_transitBackground;
drape_ptr<DrapeNotifier> m_notifier;
dp::RenderInjectionHandler m_renderInjectionHandler;
struct FrameData
{
base::Timer m_timer;
double m_frameTime = 0.0;
uint32_t m_inactiveFramesCounter = 0;
bool m_forceFullRedrawNextFrame = false;
#ifdef SHOW_FRAMES_STATS
uint64_t m_framesOverall = 0;
uint64_t m_framesFast = 0;
#endif
static uint32_t constexpr kMaxInactiveFrames = 2;
};
FrameData m_frameData;
#ifdef DEBUG
bool m_isTeardowned;
#endif
};
} // namespace df

View file

@ -0,0 +1,21 @@
#pragma once
#include "geometry/point2d.hpp"
namespace df
{
struct GpsTrackPoint
{
// Timestamp of the point, seconds from 1st Jan 1970
double m_timestamp;
// Point in the Mercator projection
m2::PointD m_point;
// Speed in the point, M/S
double m_speedMPS;
// Unique identifier of the point
uint32_t m_id;
};
} // namespace df

View file

@ -0,0 +1,316 @@
#include "drape_frontend/gps_track_renderer.hpp"
#include "drape_frontend/color_constants.hpp"
#include "drape_frontend/map_shape.hpp"
#include "drape_frontend/shape_view_params.hpp"
#include "drape_frontend/visual_params.hpp"
#include "shaders/programs.hpp"
#include "drape/vertex_array_buffer.hpp"
#include "indexer/map_style_reader.hpp"
#include <algorithm>
#include <array>
namespace df
{
namespace
{
df::ColorConstant const kTrackUnknownDistanceColor = "TrackUnknownDistance";
df::ColorConstant const kTrackCarSpeedColor = "TrackCarSpeed";
df::ColorConstant const kTrackPlaneSpeedColor = "TrackPlaneSpeed";
df::ColorConstant const kTrackHumanSpeedColor = "TrackHumanSpeed";
int constexpr kMinVisibleZoomLevel = 5;
uint32_t constexpr kAveragePointsCount = 512;
// Radius of circles depending on zoom levels.
std::array<float, 20> constexpr kRadiusInPixel = {
// 1 2 3 4 5 6 7 8 9 10
0.8f, 0.8f, 1.5f, 2.5f, 2.5f, 2.5f, 2.5f, 2.5f, 2.5f, 2.5f,
// 11 12 13 14 15 16 17 18 19 20
2.5f, 2.5f, 2.5f, 2.5f, 3.0f, 4.0f, 4.5f, 4.5f, 5.0f, 5.5f};
double constexpr kHumanSpeed = 2.6; // meters per second
double constexpr kCarSpeed = 6.2; // meters per second
uint8_t constexpr kMinDayAlpha = 90;
uint8_t constexpr kMaxDayAlpha = 144;
uint8_t constexpr kMinNightAlpha = 50;
uint8_t constexpr kMaxNightAlpha = 102;
double constexpr kUnknownDistanceTime = 5 * 60; // seconds
double constexpr kDistanceScalar = 0.4;
#ifdef DEBUG
bool GpsPointsSortPredicate(GpsTrackPoint const & pt1, GpsTrackPoint const & pt2)
{
return pt1.m_id < pt2.m_id;
}
#endif
} // namespace
GpsTrackRenderer::GpsTrackRenderer(TRenderDataRequestFn const & dataRequestFn)
: m_dataRequestFn(dataRequestFn)
, m_needUpdate(false)
, m_waitForRenderData(false)
, m_radius(0.0f)
{
ASSERT(m_dataRequestFn != nullptr, ());
m_points.reserve(kAveragePointsCount);
m_handlesCache.reserve(8);
}
void GpsTrackRenderer::AddRenderData(ref_ptr<dp::GraphicsContext> context, ref_ptr<gpu::ProgramManager> mng,
drape_ptr<CirclesPackRenderData> && renderData)
{
drape_ptr<CirclesPackRenderData> data = std::move(renderData);
ref_ptr<dp::GpuProgram> program = mng->GetProgram(gpu::Program::CirclePoint);
program->Bind();
data->m_bucket->GetBuffer()->Build(context, program);
m_renderData.push_back(std::move(data));
m_waitForRenderData = false;
}
void GpsTrackRenderer::ClearRenderData()
{
m_renderData.clear();
m_handlesCache.clear();
m_waitForRenderData = false;
m_needUpdate = true;
}
void GpsTrackRenderer::UpdatePoints(std::vector<GpsTrackPoint> const & toAdd, std::vector<uint32_t> const & toRemove)
{
bool recreateSpline = false;
if (!toRemove.empty())
{
size_t const szBefore = m_points.size();
base::EraseIf(m_points, [&toRemove](GpsTrackPoint const & pt) { return base::IsExist(toRemove, pt.m_id); });
if (szBefore > m_points.size()) // if removed any
{
recreateSpline = true;
m_needUpdate = true;
}
}
if (!toAdd.empty())
{
ASSERT(is_sorted(toAdd.begin(), toAdd.end(), GpsPointsSortPredicate), ());
ASSERT(m_points.empty() || GpsPointsSortPredicate(m_points.back(), toAdd.front()), ());
m_points.insert(m_points.end(), toAdd.begin(), toAdd.end());
m_needUpdate = true;
}
if (recreateSpline) // Recreate Spline only if Remove (Clear) was invoked.
{
m_pointsSpline = m2::Spline(m_points.size());
for (auto const & p : m_points)
m_pointsSpline.AddPoint(p.m_point);
}
else // Simple append points otherwise.
{
for (auto const & p : toAdd)
m_pointsSpline.AddPoint(p.m_point);
}
}
size_t GpsTrackRenderer::GetAvailablePointsCount() const
{
size_t pointsCount = 0;
for (size_t i = 0; i < m_renderData.size(); i++)
pointsCount += m_renderData[i]->m_pointsCount;
return pointsCount;
}
dp::Color GpsTrackRenderer::CalculatePointColor(size_t pointIndex, m2::PointD const & curPoint, double lengthFromStart,
double fullLength) const
{
ASSERT_LESS(pointIndex, m_points.size(), ());
if (pointIndex + 1 == m_points.size())
return dp::Color::Transparent();
GpsTrackPoint const & start = m_points[pointIndex];
GpsTrackPoint const & end = m_points[pointIndex + 1];
double startAlpha = kMinDayAlpha;
double endAlpha = kMaxDayAlpha;
auto const style = GetStyleReader().GetCurrentStyle();
if (style == MapStyle::MapStyleDefaultDark)
{
startAlpha = kMinNightAlpha;
endAlpha = kMaxNightAlpha;
}
double const ta = math::Clamp(lengthFromStart / fullLength, 0.0, 1.0);
double const alpha = startAlpha * (1.0 - ta) + endAlpha * ta;
if ((end.m_timestamp - start.m_timestamp) > kUnknownDistanceTime)
{
dp::Color const color = df::GetColorConstant(df::kTrackUnknownDistanceColor);
return dp::Color(color.GetRed(), color.GetGreen(), color.GetBlue(), static_cast<uint8_t>(alpha));
}
double const length = (end.m_point - start.m_point).Length();
double const dist = (curPoint - start.m_point).Length();
double const td = math::Clamp(dist / length, 0.0, 1.0);
double const speed = std::max(start.m_speedMPS * (1.0 - td) + end.m_speedMPS * td, 0.0);
dp::Color const color = GetColorBySpeed(speed);
return dp::Color(color.GetRed(), color.GetGreen(), color.GetBlue(), static_cast<uint8_t>(alpha));
}
dp::Color GpsTrackRenderer::GetColorBySpeed(double speed) const
{
if (speed > kHumanSpeed && speed <= kCarSpeed)
return df::GetColorConstant(df::kTrackCarSpeedColor);
else if (speed > kCarSpeed)
return df::GetColorConstant(df::kTrackPlaneSpeedColor);
return df::GetColorConstant(df::kTrackHumanSpeedColor);
}
void GpsTrackRenderer::RenderTrack(ref_ptr<dp::GraphicsContext> context, ref_ptr<gpu::ProgramManager> mng,
ScreenBase const & screen, int zoomLevel, FrameValues const & frameValues)
{
if (zoomLevel < kMinVisibleZoomLevel)
return;
if (m_needUpdate)
{
// Skip rendering if there is no any point.
if (m_points.empty())
{
m_needUpdate = false;
return;
}
// Check if there are render data.
if (m_renderData.empty() && !m_waitForRenderData)
{
m_dataRequestFn(kAveragePointsCount);
m_waitForRenderData = true;
}
if (m_waitForRenderData)
return;
m_radius = CalculateRadius(screen, kRadiusInPixel);
double const currentScaleGtoP = 1.0 / screen.GetScale();
double const radiusMercator = m_radius / currentScaleGtoP;
double const diameterMercator = 2.0 * radiusMercator;
double const step = diameterMercator + kDistanceScalar * diameterMercator;
// Update points' positions and colors.
ASSERT(!m_renderData.empty(), ());
m_handlesCache.clear();
for (size_t i = 0; i < m_renderData.size(); i++)
{
auto & bucket = m_renderData[i]->m_bucket;
ASSERT_EQUAL(bucket->GetOverlayHandlesCount(), 1, ());
CirclesPackHandle * handle = static_cast<CirclesPackHandle *>(bucket->GetOverlayHandle(0).get());
handle->Clear();
m_handlesCache.push_back(std::make_pair(handle, 0));
}
m_pivot = screen.GlobalRect().Center();
size_t cacheIndex = 0;
if (m_points.size() == 1)
{
dp::Color const color = GetColorBySpeed(m_points.front().m_speedMPS);
m2::PointD const pt = MapShape::ConvertToLocal(m_points.front().m_point, m_pivot, kShapeCoordScalar);
m_handlesCache[cacheIndex].first->SetPoint(0, pt, m_radius, color);
m_handlesCache[cacheIndex].second++;
}
else
{
m2::Spline::iterator it;
it.Attach(m_pointsSpline);
auto const fullLength = it.GetFullLength();
double lengthFromStart = 0.0;
while (!it.BeginAgain())
{
m2::PointD const pt = it.m_pos;
m2::RectD pointRect(pt.x - radiusMercator, pt.y - radiusMercator, pt.x + radiusMercator, pt.y + radiusMercator);
if (screen.ClipRect().IsIntersect(pointRect))
{
dp::Color const color = CalculatePointColor(it.GetIndex(), pt, lengthFromStart, fullLength);
m2::PointD const convertedPt = MapShape::ConvertToLocal(pt, m_pivot, kShapeCoordScalar);
m_handlesCache[cacheIndex].first->SetPoint(m_handlesCache[cacheIndex].second, convertedPt, m_radius, color);
m_handlesCache[cacheIndex].second++;
if (m_handlesCache[cacheIndex].second >= m_handlesCache[cacheIndex].first->GetPointsCount())
cacheIndex++;
if (cacheIndex >= m_handlesCache.size())
{
m_dataRequestFn(kAveragePointsCount);
m_waitForRenderData = true;
return;
}
}
lengthFromStart += step;
it.Advance(step);
}
#ifdef GPS_TRACK_SHOW_RAW_POINTS
for (size_t i = 0; i < m_points.size(); i++)
{
m2::PointD const convertedPt = MapShape::ConvertToLocal(m_points[i].m_point, m_pivot, kShapeCoordScalar);
m_handlesCache[cacheIndex].first->SetPoint(m_handlesCache[cacheIndex].second, convertedPt, m_radius * 1.2,
dp::Color(0, 0, 255, 255));
m_handlesCache[cacheIndex].second++;
if (m_handlesCache[cacheIndex].second >= m_handlesCache[cacheIndex].first->GetPointsCount())
cacheIndex++;
if (cacheIndex >= m_handlesCache.size())
{
m_dataRequestFn(kAveragePointsCount);
m_waitForRenderData = true;
return;
}
}
#endif
}
m_needUpdate = false;
}
if (m_handlesCache.empty() || m_handlesCache.front().second == 0)
return;
ASSERT_LESS_OR_EQUAL(m_renderData.size(), m_handlesCache.size(), ());
// Render points.
gpu::MapProgramParams params;
frameValues.SetTo(params);
math::Matrix<float, 4, 4> mv = screen.GetModelView(m_pivot, kShapeCoordScalar);
params.m_modelView = glsl::make_mat4(mv.m_data);
ref_ptr<dp::GpuProgram> program = mng->GetProgram(gpu::Program::CirclePoint);
program->Bind();
ASSERT_GREATER(m_renderData.size(), 0, ());
dp::RenderState const & state = m_renderData.front()->m_state;
dp::ApplyState(context, program, state);
mng->GetParamsSetter()->Apply(context, program, params);
for (size_t i = 0; i < m_renderData.size(); i++)
if (m_handlesCache[i].second != 0)
m_renderData[i]->m_bucket->Render(context, state.GetDrawAsLine());
}
void GpsTrackRenderer::Update()
{
m_needUpdate = true;
}
void GpsTrackRenderer::Clear()
{
m_points.clear();
m_pointsSpline.Clear();
ClearRenderData();
}
} // namespace df

View file

@ -0,0 +1,55 @@
#pragma once
#include "drape_frontend/circles_pack_shape.hpp"
#include "drape_frontend/frame_values.hpp"
#include "drape_frontend/gps_track_point.hpp"
#include "shaders/program_manager.hpp"
#include "drape/graphics_context.hpp"
#include "drape/pointers.hpp"
#include "geometry/screenbase.hpp"
#include "geometry/spline.hpp"
#include <functional>
#include <map>
#include <vector>
namespace df
{
class GpsTrackRenderer final
{
public:
using TRenderDataRequestFn = std::function<void(uint32_t)>;
explicit GpsTrackRenderer(TRenderDataRequestFn const & dataRequestFn);
void AddRenderData(ref_ptr<dp::GraphicsContext> context, ref_ptr<gpu::ProgramManager> mng,
drape_ptr<CirclesPackRenderData> && renderData);
void UpdatePoints(std::vector<GpsTrackPoint> const & toAdd, std::vector<uint32_t> const & toRemove);
void RenderTrack(ref_ptr<dp::GraphicsContext> context, ref_ptr<gpu::ProgramManager> mng, ScreenBase const & screen,
int zoomLevel, FrameValues const & frameValues);
void Update();
void Clear();
void ClearRenderData();
private:
size_t GetAvailablePointsCount() const;
dp::Color CalculatePointColor(size_t pointIndex, m2::PointD const & curPoint, double lengthFromStart,
double fullLength) const;
dp::Color GetColorBySpeed(double speed) const;
TRenderDataRequestFn m_dataRequestFn;
std::vector<drape_ptr<CirclesPackRenderData>> m_renderData;
std::vector<GpsTrackPoint> m_points;
m2::Spline m_pointsSpline;
bool m_needUpdate;
bool m_waitForRenderData;
std::vector<std::pair<CirclesPackHandle *, size_t>> m_handlesCache;
float m_radius;
m2::PointD m_pivot;
};
} // namespace df

View file

@ -0,0 +1,98 @@
#include "drape_frontend/gui/choose_position_mark.hpp"
#include "drape_frontend/gui/drape_gui.hpp"
#include "drape_frontend/batcher_bucket.hpp"
#include "shaders/programs.hpp"
#include "drape/utils/vertex_decl.hpp"
#include <functional>
#include <utility>
using namespace std::placeholders;
namespace gui
{
namespace
{
struct ChoosePositionMarkVertex
{
ChoosePositionMarkVertex(glsl::vec2 const & position, glsl::vec2 const & texCoord)
: m_position(position)
, m_texCoord(texCoord)
{}
glsl::vec2 m_position;
glsl::vec2 m_texCoord;
};
class ChoosePositionMarkHandle : public Handle
{
using TBase = Handle;
public:
ChoosePositionMarkHandle(uint32_t id, m2::PointF const & pivot) : Handle(id, dp::Center, pivot)
{
SetIsVisible(true);
}
bool Update(ScreenBase const & screen) override
{
SetPivot(glsl::ToVec2(m2::PointF(screen.PixelRectIn3d().Center())));
return TBase::Update(screen);
}
};
} // namespace
drape_ptr<ShapeRenderer> ChoosePositionMark::Draw(ref_ptr<dp::GraphicsContext> context,
ref_ptr<dp::TextureManager> tex) const
{
dp::TextureManager::SymbolRegion region;
tex->GetSymbolRegion("cross_geoposition", region);
glsl::vec2 const halfSize = glsl::ToVec2(region.GetPixelSize() * 0.5f);
m2::RectF const texRect = region.GetTexRect();
ASSERT_EQUAL(m_position.m_anchor, dp::Center, ());
ChoosePositionMarkVertex vertexes[] = {
ChoosePositionMarkVertex(glsl::vec2(-halfSize.x, halfSize.y), glsl::ToVec2(texRect.LeftTop())),
ChoosePositionMarkVertex(glsl::vec2(-halfSize.x, -halfSize.y), glsl::ToVec2(texRect.LeftBottom())),
ChoosePositionMarkVertex(glsl::vec2(halfSize.x, halfSize.y), glsl::ToVec2(texRect.RightTop())),
ChoosePositionMarkVertex(glsl::vec2(halfSize.x, -halfSize.y), glsl::ToVec2(texRect.RightBottom()))};
auto state = df::CreateRenderState(gpu::Program::TexturingGui, df::DepthLayer::GuiLayer);
state.SetColorTexture(region.GetTexture());
state.SetDepthTestEnabled(false);
state.SetTextureIndex(region.GetTextureIndex());
dp::AttributeProvider provider(1 /*streamCount*/, 4 /*vertexCount*/);
dp::BindingInfo info(2 /*count*/);
dp::BindingDecl & posDecl = info.GetBindingDecl(0);
posDecl.m_attributeName = "a_position";
posDecl.m_componentCount = 2;
posDecl.m_componentType = gl_const::GLFloatType;
posDecl.m_offset = 0;
posDecl.m_stride = sizeof(ChoosePositionMarkVertex);
dp::BindingDecl & texDecl = info.GetBindingDecl(1);
texDecl.m_attributeName = "a_colorTexCoords";
texDecl.m_componentCount = 2;
texDecl.m_componentType = gl_const::GLFloatType;
texDecl.m_offset = sizeof(glsl::vec2);
texDecl.m_stride = posDecl.m_stride;
provider.InitStream(0, info, make_ref(&vertexes));
drape_ptr<dp::OverlayHandle> handle =
make_unique_dp<ChoosePositionMarkHandle>(GuiHandleChoosePositionMark, m_position.m_pixelPivot);
drape_ptr<ShapeRenderer> renderer = make_unique_dp<ShapeRenderer>();
dp::Batcher batcher(dp::Batcher::IndexPerQuad, dp::Batcher::VertexPerQuad);
batcher.SetBatcherHash(static_cast<uint64_t>(df::BatcherBucket::Default));
dp::SessionGuard guard(context, batcher, std::bind(&ShapeRenderer::AddShape, renderer.get(), _1, _2));
batcher.InsertTriangleStrip(context, state, make_ref(&provider), std::move(handle));
return renderer;
}
} // namespace gui

View file

@ -0,0 +1,14 @@
#pragma once
#include "drape_frontend/gui/shape.hpp"
namespace gui
{
class ChoosePositionMark : public Shape
{
public:
explicit ChoosePositionMark(gui::Position const & position) : Shape(position) {}
drape_ptr<ShapeRenderer> Draw(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::TextureManager> tex) const;
};
} // namespace gui

View file

@ -0,0 +1,141 @@
#include "drape_frontend/gui/compass.hpp"
#include "drape_frontend/animation/show_hide_animation.hpp"
#include "drape_frontend/batcher_bucket.hpp"
#include "drape_frontend/gui/drape_gui.hpp"
#include "shaders/programs.hpp"
#include "drape/glsl_func.hpp"
#include "drape/glsl_types.hpp"
#include "drape/utils/vertex_decl.hpp"
#include <functional>
#include <utility>
using namespace std::placeholders;
namespace gui
{
namespace
{
struct CompassVertex
{
CompassVertex(glsl::vec2 const & position, glsl::vec2 const & texCoord) : m_position(position), m_texCoord(texCoord)
{}
glsl::vec2 m_position;
glsl::vec2 m_texCoord;
};
class CompassHandle : public TappableHandle
{
using TBase = TappableHandle;
public:
CompassHandle(uint32_t id, m2::PointF const & pivot, m2::PointF const & size, Shape::TTapHandler const & tapHandler)
: TappableHandle(id, dp::Center, pivot, size)
, m_tapHandler(tapHandler)
, m_animation(false, 0.25)
{}
void OnTap() override
{
if (m_tapHandler != nullptr)
m_tapHandler();
}
bool Update(ScreenBase const & screen) override
{
static double constexpr kVisibleTolerance = 0.1;
static double constexpr kVisibleStartAngle = math::DegToRad(0.0 + kVisibleTolerance);
static double constexpr kVisibleEndAngle = math::DegToRad(360.0 - kVisibleTolerance);
auto const angle = static_cast<float>(ang::AngleIn2PI(screen.GetAngle()));
bool isVisiblePrev = IsVisible();
bool isVisibleAngle = angle > kVisibleStartAngle && angle < kVisibleEndAngle;
bool isVisible = isVisibleAngle || (isVisiblePrev && DrapeGui::Instance().IsInUserAction());
if (isVisible)
{
m_animation.ShowAnimated();
SetIsVisible(true);
}
else
m_animation.HideAnimated();
if (IsVisible())
{
TBase::Update(screen);
glsl::mat4 r = glsl::rotate(glsl::mat4(), angle, glsl::vec3(0.0, 0.0, 1.0));
glsl::mat4 m = glsl::translate(glsl::mat4(), glsl::vec3(m_pivot, 0.0));
m_params.m_modelView = glsl::transpose(m * r);
m_params.m_opacity = static_cast<float>(m_animation.GetT());
}
if (m_animation.IsFinished())
SetIsVisible(isVisible);
return true;
}
private:
Shape::TTapHandler m_tapHandler;
df::ShowHideAnimation m_animation;
};
} // namespace
drape_ptr<ShapeRenderer> Compass::Draw(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::TextureManager> tex,
TTapHandler const & tapHandler) const
{
dp::TextureManager::SymbolRegion region;
tex->GetSymbolRegion("compass", region);
auto const halfSize = glsl::ToVec2(region.GetPixelSize() * 0.5f);
auto const texRect = region.GetTexRect();
ASSERT_EQUAL(m_position.m_anchor, dp::Center, ());
CompassVertex vertexes[] = {CompassVertex(glsl::vec2(-halfSize.x, halfSize.y), glsl::ToVec2(texRect.LeftTop())),
CompassVertex(glsl::vec2(-halfSize.x, -halfSize.y), glsl::ToVec2(texRect.LeftBottom())),
CompassVertex(glsl::vec2(halfSize.x, halfSize.y), glsl::ToVec2(texRect.RightTop())),
CompassVertex(glsl::vec2(halfSize.x, -halfSize.y), glsl::ToVec2(texRect.RightBottom()))};
auto state = df::CreateRenderState(gpu::Program::TexturingGui, df::DepthLayer::GuiLayer);
state.SetColorTexture(region.GetTexture());
state.SetDepthTestEnabled(false);
state.SetTextureIndex(region.GetTextureIndex());
dp::AttributeProvider provider(1, 4);
dp::BindingInfo info(2);
dp::BindingDecl & posDecl = info.GetBindingDecl(0);
posDecl.m_attributeName = "a_position";
posDecl.m_componentCount = 2;
posDecl.m_componentType = gl_const::GLFloatType;
posDecl.m_offset = 0;
posDecl.m_stride = sizeof(CompassVertex);
dp::BindingDecl & texDecl = info.GetBindingDecl(1);
texDecl.m_attributeName = "a_colorTexCoords";
texDecl.m_componentCount = 2;
texDecl.m_componentType = gl_const::GLFloatType;
texDecl.m_offset = sizeof(glsl::vec2);
texDecl.m_stride = posDecl.m_stride;
provider.InitStream(0, info, make_ref(&vertexes));
drape_ptr<dp::OverlayHandle> handle = make_unique_dp<CompassHandle>(
EGuiHandle::GuiHandleCompass, m_position.m_pixelPivot, region.GetPixelSize(), tapHandler);
drape_ptr<ShapeRenderer> renderer = make_unique_dp<ShapeRenderer>();
dp::Batcher batcher(dp::Batcher::IndexPerQuad, dp::Batcher::VertexPerQuad);
batcher.SetBatcherHash(static_cast<uint64_t>(df::BatcherBucket::Default));
dp::SessionGuard guard(context, batcher, std::bind(&ShapeRenderer::AddShape, renderer.get(), _1, _2));
batcher.InsertTriangleStrip(context, state, make_ref(&provider), std::move(handle));
return renderer;
}
} // namespace gui

View file

@ -0,0 +1,15 @@
#pragma once
#include "drape_frontend/gui/shape.hpp"
namespace gui
{
class Compass : public Shape
{
public:
explicit Compass(gui::Position const & position) : Shape(position) {}
drape_ptr<ShapeRenderer> Draw(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::TextureManager> tex,
TTapHandler const & tapHandler) const;
};
} // namespace gui

View file

@ -0,0 +1,92 @@
#include "copyright_label.hpp"
#include "drape_gui.hpp"
#include "gui_text.hpp"
#include "drape_frontend/animation/opacity_animation.hpp"
#include "drape_frontend/batcher_bucket.hpp"
#include <functional>
#include <utility>
namespace gui
{
namespace
{
constexpr double kCopyrightVisibleTime = 10.0f;
constexpr double kCopyrightHideTime = 0.25f;
class CopyrightHandle : public StaticLabelHandle
{
using TBase = StaticLabelHandle;
public:
CopyrightHandle(uint32_t id, ref_ptr<dp::TextureManager> textureManager, dp::Anchor anchor, m2::PointF const & pivot,
dp::TGlyphs && glyphs)
: TBase(id, textureManager, anchor, pivot, std::move(glyphs))
{
SetIsVisible(true);
}
bool Update(ScreenBase const & screen) override
{
if (!IsVisible())
return false;
if (!TBase::Update(screen))
return false;
if (!DrapeGui::Instance().IsCopyrightActive())
{
SetIsVisible(false);
return false;
}
if (m_animation == nullptr)
{
m_animation = make_unique_dp<df::OpacityAnimation>(kCopyrightHideTime, kCopyrightVisibleTime, 1.0f, 0.0f);
}
else if (m_animation->IsFinished())
{
DrapeGui::Instance().DeactivateCopyright();
SetIsVisible(false);
}
m_params.m_opacity = static_cast<float>(m_animation->GetOpacity());
return true;
}
private:
drape_ptr<df::OpacityAnimation> m_animation;
};
} // namespace
CopyrightLabel::CopyrightLabel(Position const & position) : TBase(position) {}
drape_ptr<ShapeRenderer> CopyrightLabel::Draw(ref_ptr<dp::GraphicsContext> context,
ref_ptr<dp::TextureManager> tex) const
{
StaticLabel::LabelResult result;
auto glyphs = StaticLabel::CacheStaticText("Map data © OpenStreetMap", "", m_position.m_anchor,
DrapeGui::GetGuiTextFont(), tex, result);
dp::AttributeProvider provider(1 /*stream count*/, static_cast<uint32_t>(result.m_buffer.size()));
provider.InitStream(0 /*stream index*/, StaticLabel::Vertex::GetBindingInfo(), make_ref(result.m_buffer.data()));
auto const vertexCount = static_cast<uint32_t>(result.m_buffer.size());
ASSERT(vertexCount % dp::Batcher::VertexPerQuad == 0, ());
auto const indexCount = dp::Batcher::IndexPerQuad * vertexCount / dp::Batcher::VertexPerQuad;
drape_ptr<dp::OverlayHandle> handle = make_unique_dp<CopyrightHandle>(GuiHandleCopyright, tex, m_position.m_anchor,
m_position.m_pixelPivot, std::move(glyphs));
drape_ptr<ShapeRenderer> renderer = make_unique_dp<ShapeRenderer>();
dp::Batcher batcher(indexCount, vertexCount);
batcher.SetBatcherHash(static_cast<uint64_t>(df::BatcherBucket::Default));
using namespace std::placeholders;
dp::SessionGuard guard(context, batcher, std::bind(&ShapeRenderer::AddShape, renderer.get(), _1, _2));
batcher.InsertListOfStrip(context, result.m_state, make_ref(&provider), std::move(handle),
dp::Batcher::VertexPerQuad);
return renderer;
}
} // namespace gui

View file

@ -0,0 +1,15 @@
#pragma once
#include "drape_frontend/gui/shape.hpp"
namespace gui
{
class CopyrightLabel : public Shape
{
using TBase = Shape;
public:
explicit CopyrightLabel(Position const & position);
drape_ptr<ShapeRenderer> Draw(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::TextureManager> tex) const;
};
} // namespace gui

View file

@ -0,0 +1,96 @@
#include "drape_frontend/gui/debug_label.hpp"
#include "drape_frontend/gui/drape_gui.hpp"
#include <functional>
#include <set>
#include <utility>
using namespace std::placeholders;
namespace gui
{
class DebugLabelHandle : public MutableLabelHandle
{
using TBase = MutableLabelHandle;
public:
DebugLabelHandle(uint32_t id, dp::Anchor anchor, m2::PointF const & pivot, ref_ptr<dp::TextureManager> tex,
TUpdateDebugLabelFn const & onUpdateFn)
: MutableLabelHandle(id, anchor, pivot)
, m_onUpdateFn(onUpdateFn)
{
SetTextureManager(tex);
}
bool Update(ScreenBase const & screen) override
{
std::string content;
bool const isVisible = m_onUpdateFn(screen, content);
SetIsVisible(isVisible);
SetContent(content);
return TBase::Update(screen);
}
private:
TUpdateDebugLabelFn m_onUpdateFn;
};
void AddSymbols(std::string const & str, std::set<char> & symbols)
{
for (size_t i = 0, sz = str.length(); i < sz; ++i)
symbols.insert(str[i]);
}
void DebugInfoLabels::AddLabel(ref_ptr<dp::TextureManager> tex, std::string const & caption,
TUpdateDebugLabelFn const & onUpdateFn)
{
std::string alphabet;
std::set<char> symbols;
AddSymbols(caption, symbols);
AddSymbols("0123456789.-e", symbols);
alphabet.reserve(symbols.size());
for (auto const & ch : symbols)
alphabet.push_back(ch);
MutableLabelDrawer::Params params;
params.m_anchor = dp::LeftTop;
params.m_alphabet = alphabet;
params.m_maxLength = 100;
params.m_font = DrapeGui::GetGuiTextFont();
params.m_font.m_color = dp::Color(0, 0, 255, 255);
params.m_font.m_size *= 1.2;
params.m_pivot = m_position.m_pixelPivot;
#ifdef RENDER_DEBUG_INFO_LABELS
uint32_t const id = GuiHandleDebugLabel + m_labelsCount;
params.m_handleCreator = [id, onUpdateFn, tex](dp::Anchor anchor, m2::PointF const & pivot)
{ return make_unique_dp<DebugLabelHandle>(id, anchor, pivot, tex, onUpdateFn); };
#endif
m_labelsParams.push_back(params);
++m_labelsCount;
}
drape_ptr<ShapeRenderer> DebugInfoLabels::Draw(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::TextureManager> tex)
{
drape_ptr<ShapeRenderer> renderer = make_unique_dp<ShapeRenderer>();
m2::PointF pos = m_position.m_pixelPivot;
for (auto & params : m_labelsParams)
{
params.m_pivot.y = pos.y;
m2::PointF textSize =
MutableLabelDrawer::Draw(context, params, tex, std::bind(&ShapeRenderer::AddShape, renderer.get(), _1, _2));
pos.y += 2 * textSize.y;
}
m_labelsParams.clear();
return renderer;
}
} // namespace gui

View file

@ -0,0 +1,26 @@
#pragma once
#include "drape_frontend/gui/gui_text.hpp"
#include "drape_frontend/gui/shape.hpp"
#include <functional>
#include <string>
#include <vector>
namespace gui
{
using TUpdateDebugLabelFn = std::function<bool(ScreenBase const & screen, std::string & content)>;
class DebugInfoLabels : public Shape
{
public:
explicit DebugInfoLabels(gui::Position const & position) : Shape(position) {}
void AddLabel(ref_ptr<dp::TextureManager> tex, std::string const & caption, TUpdateDebugLabelFn const & onUpdateFn);
drape_ptr<ShapeRenderer> Draw(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::TextureManager> tex);
private:
std::vector<MutableLabelDrawer::Params> m_labelsParams;
uint32_t m_labelsCount = 0;
};
} // namespace gui

View file

@ -0,0 +1,73 @@
#include "drape_gui.hpp"
#include "ruler_helper.hpp"
#include "drape_frontend/color_constants.hpp"
#include "drape_frontend/visual_params.hpp"
#include "base/assert.hpp"
namespace gui
{
df::ColorConstant const kGuiTextColor = "GuiText";
struct DrapeGui::Impl
{
RulerHelper m_rulerHelper;
};
DrapeGui::DrapeGui() : m_impl(new Impl()) {}
DrapeGui & DrapeGui::Instance()
{
static DrapeGui s_gui;
if (!s_gui.m_impl)
s_gui.m_impl.reset(new Impl());
return s_gui;
}
RulerHelper & DrapeGui::GetRulerHelper()
{
return Instance().GetRulerHelperImpl();
}
dp::FontDecl DrapeGui::GetGuiTextFont()
{
return {df::GetColorConstant(kGuiTextColor), 14};
}
void DrapeGui::Destroy()
{
ASSERT(m_impl != nullptr, ());
m_impl.reset();
}
void DrapeGui::SetSurfaceSize(m2::PointF const & size)
{
std::lock_guard<std::mutex> lock(m_surfaceSizeMutex);
m_surfaceSize = size;
}
m2::PointF DrapeGui::GetSurfaceSize() const
{
std::lock_guard<std::mutex> lock(m_surfaceSizeMutex);
return m_surfaceSize;
}
RulerHelper & DrapeGui::GetRulerHelperImpl()
{
ASSERT(m_impl != nullptr, ());
return m_impl->m_rulerHelper;
}
void DrapeGui::ConnectOnCompassTappedHandler(Shape::TTapHandler const & handler)
{
m_onCompassTappedHandler = handler;
}
void DrapeGui::CallOnCompassTappedHandler()
{
if (m_onCompassTappedHandler != nullptr)
m_onCompassTappedHandler();
}
} // namespace gui

View file

@ -0,0 +1,59 @@
#pragma once
#include "drape_frontend/gui/compass.hpp"
#include "drape_frontend/gui/scale_fps_helper.hpp"
#include "drape_frontend/gui/skin.hpp"
#include "storage/storage_defines.hpp"
#include "drape/pointers.hpp"
#include <functional>
#include <memory>
#include <mutex>
class ScreenBase;
namespace gui
{
class RulerHelper;
class DrapeGui
{
public:
static DrapeGui & Instance();
static RulerHelper & GetRulerHelper();
static dp::FontDecl GetGuiTextFont();
void Destroy();
void SetSurfaceSize(m2::PointF const & size);
m2::PointF GetSurfaceSize() const;
bool IsInUserAction() const { return m_inUserAction; }
void SetInUserAction(bool isInUserAction) { m_inUserAction = isInUserAction; }
bool IsCopyrightActive() const { return m_isCopyrightActive; }
void DeactivateCopyright() { m_isCopyrightActive = false; }
void ConnectOnCompassTappedHandler(Shape::TTapHandler const & handler);
void CallOnCompassTappedHandler();
ScaleFpsHelper & GetScaleFpsHelper() { return m_scaleFpsHelper; }
ScaleFpsHelper const & GetScaleFpsHelper() const { return m_scaleFpsHelper; }
private:
DrapeGui();
RulerHelper & GetRulerHelperImpl();
struct Impl;
std::unique_ptr<Impl> m_impl;
bool m_isCopyrightActive = true;
Shape::TTapHandler m_onCompassTappedHandler;
m2::PointF m_surfaceSize;
mutable std::mutex m_surfaceSizeMutex;
bool m_inUserAction = false;
ScaleFpsHelper m_scaleFpsHelper;
};
} // namespace gui

View file

@ -0,0 +1,565 @@
#include "drape_frontend/gui/gui_text.hpp"
#include "drape_frontend/batcher_bucket.hpp"
#include "drape_frontend/visual_params.hpp"
#include "shaders/programs.hpp"
#include "base/stl_helpers.hpp"
#include "drape/font_constants.hpp"
#include <algorithm>
#include <array>
#include <memory>
namespace gui
{
namespace
{
glsl::vec2 GetNormalsAndMask(dp::TextureManager::GlyphRegion const & glyph, float xOffset, float yOffset,
float textRatio, std::array<glsl::vec2, 4> & normals,
std::array<glsl::vec2, 4> & maskTexCoord)
{
m2::PointF const pixelSize = glyph.GetPixelSize() * textRatio;
m2::RectF const & r = glyph.GetTexRect();
xOffset *= textRatio;
yOffset *= textRatio;
float const upVector = -pixelSize.y - yOffset;
float const bottomVector = -yOffset;
normals[0] = glsl::vec2(xOffset, bottomVector);
normals[1] = glsl::vec2(xOffset, upVector);
normals[2] = glsl::vec2(pixelSize.x + xOffset, bottomVector);
normals[3] = glsl::vec2(pixelSize.x + xOffset, upVector);
maskTexCoord[0] = glsl::ToVec2(r.LeftTop());
maskTexCoord[1] = glsl::ToVec2(r.LeftBottom());
maskTexCoord[2] = glsl::ToVec2(r.RightTop());
maskTexCoord[3] = glsl::ToVec2(r.RightBottom());
return {xOffset, yOffset};
}
void FillCommonDecl(dp::BindingDecl & decl, std::string const & name, uint8_t compCount, uint8_t stride, uint8_t offset)
{
decl.m_attributeName = name;
decl.m_componentCount = compCount;
decl.m_componentType = gl_const::GLFloatType;
decl.m_stride = stride;
decl.m_offset = offset;
}
void FillPositionDecl(dp::BindingDecl & decl, uint8_t stride, uint8_t offset)
{
FillCommonDecl(decl, "a_position", 3, stride, offset);
}
void FillNormalDecl(dp::BindingDecl & decl, uint8_t stride, uint8_t offset)
{
FillCommonDecl(decl, "a_normal", 2, stride, offset);
}
void FillColorDecl(dp::BindingDecl & decl, uint8_t stride, uint8_t offset)
{
FillCommonDecl(decl, "a_colorTexCoord", 2, stride, offset);
}
void FillOutlineDecl(dp::BindingDecl & decl, uint8_t stride, uint8_t offset)
{
FillCommonDecl(decl, "a_outlineColorTexCoord", 2, stride, offset);
}
void FillMaskDecl(dp::BindingDecl & decl, uint8_t stride, uint8_t offset)
{
FillCommonDecl(decl, "a_maskTexCoord", 2, stride, offset);
}
} // namespace
dp::BindingInfo const & StaticLabel::Vertex::GetBindingInfo()
{
static std::unique_ptr<dp::BindingInfo> info;
if (info == nullptr)
{
info = std::make_unique<dp::BindingInfo>(5);
uint8_t constexpr stride = sizeof(Vertex);
uint8_t offset = 0;
FillPositionDecl(info->GetBindingDecl(0), stride, offset);
offset += sizeof(glsl::vec3);
FillColorDecl(info->GetBindingDecl(1), stride, offset);
offset += sizeof(glsl::vec2);
FillOutlineDecl(info->GetBindingDecl(2), stride, offset);
offset += sizeof(glsl::vec2);
FillNormalDecl(info->GetBindingDecl(3), stride, offset);
offset += sizeof(glsl::vec2);
FillMaskDecl(info->GetBindingDecl(4), stride, offset);
ASSERT_EQUAL(offset + sizeof(glsl::vec2), stride, ());
}
return *info;
}
StaticLabel::LabelResult::LabelResult()
: m_state(df::CreateRenderState(gpu::Program::TextStaticOutlinedGui, df::DepthLayer::GuiLayer))
{
m_state.SetDepthTestEnabled(false);
}
dp::TGlyphs StaticLabel::CacheStaticText(std::string const & text, char const * delimiters, dp::Anchor anchor,
dp::FontDecl const & font, ref_ptr<dp::TextureManager> mng,
LabelResult & result)
{
ASSERT(!text.empty(), ());
auto const textRatio =
font.m_size * static_cast<float>(df::VisualParams::Instance().GetVisualScale() / dp::kBaseFontSizePixels);
dp::TextureManager::TMultilineGlyphsBuffer buffers;
auto const shapedLines = mng->ShapeMultilineText(dp::kBaseFontSizePixels, text, delimiters, buffers);
ASSERT_EQUAL(shapedLines.size(), buffers.size(), ());
#ifdef DEBUG
for (size_t i = 0; i < buffers.size(); ++i)
{
ASSERT(!buffers[i].empty(), ());
ASSERT_EQUAL(buffers[i].size(), shapedLines[i].m_glyphs.size(), ());
}
ref_ptr<dp::Texture> texture = buffers[0][0].GetTexture();
for (dp::TextureManager::TGlyphsBuffer const & b : buffers)
for (dp::TextureManager::GlyphRegion const & reg : b)
ASSERT(texture == reg.GetTexture(), ());
#endif
dp::TextureManager::ColorRegion color;
dp::TextureManager::ColorRegion outline;
mng->GetColorRegion(font.m_color, color);
mng->GetColorRegion(font.m_outlineColor, outline);
ASSERT(color.GetTexture() == outline.GetTexture(), ());
glsl::vec2 colorTex = glsl::ToVec2(color.GetTexRect().Center());
glsl::vec2 outlineTex = glsl::ToVec2(outline.GetTexRect().Center());
buffer_vector<float, 4> lineLengths;
lineLengths.reserve(buffers.size());
buffer_vector<size_t, 4> ranges;
ranges.reserve(buffers.size());
float fullHeight = 0.0;
buffer_vector<Vertex, 128> & rb = result.m_buffer;
for (int i = static_cast<int>(buffers.size()) - 1; i >= 0; --i)
{
auto const & glyphs = shapedLines[i].m_glyphs;
dp::TextureManager::TGlyphsBuffer & regions = buffers[i];
lineLengths.push_back(0.0f);
float & currentLineLength = lineLengths.back();
float depth = 0.0;
glsl::vec2 pen(0.0, -fullHeight);
float prevLineHeight = 0.0;
for (size_t j = 0; j < regions.size(); ++j)
{
auto const & glyphMetrics = glyphs[j];
std::array<glsl::vec2, 4> normals, maskTex;
dp::TextureManager::GlyphRegion const & glyph = regions[j];
glsl::vec2 offsets =
GetNormalsAndMask(glyph, glyphMetrics.m_xOffset, glyphMetrics.m_yOffset, textRatio, normals, maskTex);
glsl::vec3 position = glsl::vec3(0.0, 0.0, depth);
for (size_t v = 0; v < normals.size(); ++v)
rb.emplace_back(position, colorTex, outlineTex, pen + normals[v], maskTex[v]);
float const advance = glyphMetrics.m_xAdvance * textRatio;
prevLineHeight = std::max(prevLineHeight, offsets.y + glyph.GetPixelHeight() * textRatio);
pen += glsl::vec2(advance, glyphMetrics.m_yAdvance * textRatio);
depth += 10.0f;
if (j == 0)
currentLineLength += (glyph.GetPixelSize().x * textRatio + offsets.x);
else
currentLineLength += advance;
if (j == regions.size() - 1)
currentLineLength += offsets.x;
}
ranges.push_back(rb.size());
fullHeight += prevLineHeight;
}
float const halfHeight = 0.5f * fullHeight;
float yOffset = halfHeight;
if (anchor & dp::Top)
yOffset = fullHeight;
else if (anchor & dp::Bottom)
yOffset = 0.0f;
float maxLineLength = 0.0;
size_t startIndex = 0;
for (size_t i = 0; i < ranges.size(); ++i)
{
maxLineLength = std::max(lineLengths[i], maxLineLength);
float xOffset = -lineLengths[i] / 2.0f;
if (anchor & dp::Left)
xOffset = 0;
else if (anchor & dp::Right)
xOffset += xOffset;
size_t endIndex = ranges[i];
for (size_t j = startIndex; j < endIndex; ++j)
{
rb[j].m_normal = rb[j].m_normal + glsl::vec2(xOffset, yOffset);
result.m_boundRect.Add(glsl::ToPoint(rb[j].m_normal));
}
startIndex = endIndex;
}
result.m_state.SetColorTexture(color.GetTexture());
result.m_state.SetMaskTexture(buffers[0][0].GetTexture());
dp::TGlyphs glyphs;
for (auto const & line : shapedLines)
for (auto const & glyph : line.m_glyphs)
glyphs.emplace_back(glyph.m_key);
base::SortUnique(glyphs);
return glyphs;
}
dp::BindingInfo const & MutableLabel::StaticVertex::GetBindingInfo()
{
static std::unique_ptr<dp::BindingInfo> info;
if (info == nullptr)
{
info = std::make_unique<dp::BindingInfo>(3);
uint8_t constexpr stride = sizeof(StaticVertex);
uint8_t offset = 0;
FillPositionDecl(info->GetBindingDecl(0), stride, offset);
offset += sizeof(glsl::vec3);
FillColorDecl(info->GetBindingDecl(1), stride, offset);
offset += sizeof(glsl::vec2);
FillOutlineDecl(info->GetBindingDecl(2), stride, offset);
ASSERT_EQUAL(offset + sizeof(glsl::vec2), stride, ());
}
return *info;
}
dp::BindingInfo const & MutableLabel::DynamicVertex::GetBindingInfo()
{
static std::unique_ptr<dp::BindingInfo> info;
if (info == nullptr)
{
info = std::make_unique<dp::BindingInfo>(2, 1);
uint8_t constexpr stride = sizeof(DynamicVertex);
uint8_t offset = 0;
FillNormalDecl(info->GetBindingDecl(0), stride, offset);
offset += sizeof(glsl::vec2);
FillMaskDecl(info->GetBindingDecl(1), stride, offset);
ASSERT_EQUAL(offset + sizeof(glsl::vec2), stride, ());
}
return *info;
}
MutableLabel::PrecacheResult::PrecacheResult()
: m_state(CreateRenderState(gpu::Program::TextOutlinedGui, df::DepthLayer::GuiLayer))
{
m_state.SetDepthTestEnabled(false);
}
MutableLabel::MutableLabel(dp::Anchor anchor) : m_anchor(anchor) {}
void MutableLabel::SetMaxLength(uint16_t maxLength)
{
m_maxLength = maxLength;
}
dp::TGlyphs MutableLabel::GetGlyphs() const
{
dp::TGlyphs glyphs;
glyphs.reserve(m_shapedText.m_glyphs.size());
for (auto const & glyph : m_shapedText.m_glyphs)
glyphs.emplace_back(glyph.m_key);
return glyphs;
}
void MutableLabel::Precache(PrecacheParams const & params, PrecacheResult & result, ref_ptr<dp::TextureManager> mng)
{
SetMaxLength(static_cast<uint16_t>(params.m_maxLength));
m_textRatio = params.m_font.m_size * static_cast<float>(df::VisualParams::Instance().GetVisualScale()) /
dp::kBaseFontSizePixels;
// TODO(AB): Is this shaping/precaching really needed if the text changes every frame?
m_shapedText = mng->ShapeSingleTextLine(dp::kBaseFontSizePixels, params.m_alphabet, &m_glyphRegions);
auto const firstTexture = m_glyphRegions.front().GetTexture();
#ifdef DEBUG
for (auto const & region : m_glyphRegions)
ASSERT_EQUAL(firstTexture, region.GetTexture(), ());
#endif
result.m_state.SetMaskTexture(firstTexture);
dp::TextureManager::ColorRegion color;
dp::TextureManager::ColorRegion outlineColor;
mng->GetColorRegion(params.m_font.m_color, color);
mng->GetColorRegion(params.m_font.m_outlineColor, outlineColor);
result.m_state.SetColorTexture(color.GetTexture());
glsl::vec2 colorTex = glsl::ToVec2(color.GetTexRect().Center());
glsl::vec2 outlineTex = glsl::ToVec2(outlineColor.GetTexRect().Center());
auto const vertexCount = m_maxLength * dp::Batcher::VertexPerQuad;
result.m_buffer.resize(vertexCount, StaticVertex(glsl::vec3(0.0, 0.0, 0.0), colorTex, outlineTex));
float depth = 0.0f;
for (size_t i = 0; i < vertexCount; i += 4)
{
result.m_buffer[i + 0].m_position.z = depth;
result.m_buffer[i + 1].m_position.z = depth;
result.m_buffer[i + 2].m_position.z = depth;
result.m_buffer[i + 3].m_position.z = depth;
depth += 10.0f;
}
result.m_maxPixelSize = m2::PointF(m_shapedText.m_lineWidthInPixels, m_shapedText.m_maxLineHeightInPixels);
}
void MutableLabel::SetText(LabelResult & result, std::string text, ref_ptr<dp::TextureManager> mng)
{
if (size_t const sz = text.size(); sz < m_maxLength)
{
/// @todo I don't see a better way to clear cached vertices from the previous frame (text value).
text.append(m_maxLength - sz, ' ');
}
else if (sz > m_maxLength)
{
text.erase(m_maxLength - 3);
text.append("...");
}
// TODO(AB): Calculate only the length for pre-cached glyphs in a simpler way?
m_glyphRegions.clear();
m_shapedText = mng->ShapeSingleTextLine(dp::kBaseFontSizePixels, text, &m_glyphRegions);
// TODO(AB): Reuse pre-calculated width and height?
// float maxHeight = m_shapedText.m_maxLineHeightInPixels;
// float length = m_shapedText.m_lineWidthInPixels;
float maxHeight = 0.0f;
std::pair minMaxXPos = {std::numeric_limits<float>::max(), std::numeric_limits<float>::lowest()};
float offsetLeft = 0;
glsl::vec2 pen = glsl::vec2(0.0, 0.0);
ASSERT_EQUAL(m_glyphRegions.size(), m_shapedText.m_glyphs.size(), ());
for (size_t i = 0; i < m_glyphRegions.size(); ++i)
{
std::array<glsl::vec2, 4> normals, maskTex;
dp::TextureManager::GlyphRegion const & glyph = m_glyphRegions[i];
auto const & metrics = m_shapedText.m_glyphs[i];
glsl::vec2 const offsets =
GetNormalsAndMask(glyph, metrics.m_xOffset, metrics.m_yOffset, m_textRatio, normals, maskTex);
ASSERT_EQUAL(normals.size(), maskTex.size(), ());
for (size_t j = 0; j < normals.size(); ++j)
{
result.m_buffer.emplace_back(pen + normals[j], maskTex[j]);
auto const & back = result.m_buffer.back();
if (back.m_normal.x < minMaxXPos.first)
{
minMaxXPos.first = back.m_normal.x;
offsetLeft = offsets.x;
}
minMaxXPos.second = std::max(minMaxXPos.second, back.m_normal.x);
}
// TODO(AB): yAdvance is always zero for horizontal layouts.
pen += glsl::vec2(metrics.m_xAdvance * m_textRatio, metrics.m_yAdvance * m_textRatio);
maxHeight = std::max(maxHeight, offsets.y + glyph.GetPixelHeight() * m_textRatio);
}
float const length = minMaxXPos.second - minMaxXPos.first;
// "- offset_left" is an approximation
// A correct version should be
// "- (offset_first_symbol_from_left + offset_last_symbol_from_right) / 2"
// But there is no possibility to determine the offset of the last symbol from the right.
// We only have x_offset which is "offset from left"
glsl::vec2 anchorModifier = glsl::vec2(-length / 2.0f - offsetLeft, maxHeight / 2.0f);
if (m_anchor & dp::Right)
anchorModifier.x = -length;
else if (m_anchor & dp::Left)
anchorModifier.x = 0;
if (m_anchor & dp::Top)
anchorModifier.y = maxHeight;
else if (m_anchor & dp::Bottom)
anchorModifier.y = 0;
for (DynamicVertex & v : result.m_buffer)
{
v.m_normal += anchorModifier;
result.m_boundRect.Add(glsl::ToPoint(v.m_normal));
}
}
MutableLabelHandle::MutableLabelHandle(uint32_t id, dp::Anchor anchor, m2::PointF const & pivot)
: TBase(id, anchor, pivot)
, m_textView(make_unique_dp<MutableLabel>(anchor))
, m_isContentDirty(true)
, m_glyphsReady(false)
{}
MutableLabelHandle::MutableLabelHandle(uint32_t id, dp::Anchor anchor, m2::PointF const & pivot,
ref_ptr<dp::TextureManager> textures)
: MutableLabelHandle(id, anchor, pivot)
{
m_textureManager = std::move(textures);
}
void MutableLabelHandle::GetAttributeMutation(ref_ptr<dp::AttributeBufferMutator> mutator) const
{
if (!m_isContentDirty)
return;
m_isContentDirty = false;
MutableLabel::LabelResult result;
m_textView->SetText(result, m_content, m_textureManager);
size_t const byteCount = result.m_buffer.size() * sizeof(MutableLabel::DynamicVertex);
auto const dataPointer = static_cast<MutableLabel::DynamicVertex *>(mutator->AllocateMutationBuffer(byteCount));
std::copy(result.m_buffer.begin(), result.m_buffer.end(), dataPointer);
dp::BindingInfo const & binding = MutableLabel::DynamicVertex::GetBindingInfo();
auto const & node = GetOffsetNode(binding.GetID());
ASSERT_EQUAL(node.first.GetElementSize(), sizeof(MutableLabel::DynamicVertex), ());
ASSERT_EQUAL(node.second.m_count, result.m_buffer.size(), ());
dp::MutateNode mutateNode;
mutateNode.m_data = make_ref(dataPointer);
mutateNode.m_region = node.second;
mutator->AddMutation(node.first, mutateNode);
}
bool MutableLabelHandle::Update(ScreenBase const & screen)
{
if (!m_glyphsReady)
m_glyphsReady = m_textureManager->AreGlyphsReady(m_textView->GetGlyphs());
if (!m_glyphsReady)
return false;
return TBase::Update(screen);
}
void MutableLabelHandle::SetTextureManager(ref_ptr<dp::TextureManager> textures)
{
m_textureManager = textures;
}
ref_ptr<MutableLabel> MutableLabelHandle::GetTextView() const
{
return make_ref(m_textView);
}
void MutableLabelHandle::SetContent(std::string && content)
{
if (m_content != content)
{
m_isContentDirty = true;
m_content = std::move(content);
}
}
void MutableLabelHandle::SetContent(std::string const & content)
{
if (m_content != content)
{
m_isContentDirty = true;
m_content = content;
}
}
m2::PointF MutableLabelDrawer::Draw(ref_ptr<dp::GraphicsContext> context, Params const & params,
ref_ptr<dp::TextureManager> mng, dp::Batcher::TFlushFn && flushFn)
{
uint32_t const vertexCount = dp::Batcher::VertexPerQuad * params.m_maxLength;
uint32_t const indexCount = dp::Batcher::IndexPerQuad * params.m_maxLength;
ASSERT(params.m_handleCreator != nullptr, ());
drape_ptr<MutableLabelHandle> handle = params.m_handleCreator(params.m_anchor, params.m_pivot);
MutableLabel::PrecacheParams preCacheP;
preCacheP.m_alphabet = params.m_alphabet;
preCacheP.m_font = params.m_font;
preCacheP.m_maxLength = params.m_maxLength;
MutableLabel::PrecacheResult staticData;
handle->GetTextView()->Precache(preCacheP, staticData, mng);
ASSERT_EQUAL(vertexCount, staticData.m_buffer.size(), ());
buffer_vector<MutableLabel::DynamicVertex, 128> dynData;
dynData.resize(staticData.m_buffer.size());
dp::BindingInfo const & sBinding = MutableLabel::StaticVertex::GetBindingInfo();
dp::BindingInfo const & dBinding = MutableLabel::DynamicVertex::GetBindingInfo();
dp::AttributeProvider provider(2 /*stream count*/, static_cast<uint32_t>(staticData.m_buffer.size()));
provider.InitStream(0 /*stream index*/, sBinding, make_ref(staticData.m_buffer.data()));
provider.InitStream(1 /*stream index*/, dBinding, make_ref(dynData.data()));
{
dp::Batcher batcher(indexCount, vertexCount);
batcher.SetBatcherHash(static_cast<uint64_t>(df::BatcherBucket::Default));
dp::SessionGuard const guard(context, batcher, std::move(flushFn));
batcher.InsertListOfStrip(context, staticData.m_state, make_ref(&provider), std::move(handle),
dp::Batcher::VertexPerQuad);
}
return staticData.m_maxPixelSize;
}
StaticLabelHandle::StaticLabelHandle(uint32_t id, ref_ptr<dp::TextureManager> textureManager, dp::Anchor anchor,
m2::PointF const & pivot, dp::TGlyphs && glyphs)
: TBase(id, anchor, pivot)
, m_glyphs(std::move(glyphs))
, m_textureManager(std::move(textureManager))
, m_glyphsReady(false)
{}
bool StaticLabelHandle::Update(ScreenBase const & screen)
{
if (!m_glyphsReady)
m_glyphsReady = m_textureManager->AreGlyphsReady(m_glyphs);
if (!m_glyphsReady)
return false;
return TBase::Update(screen);
}
} // namespace gui

View file

@ -0,0 +1,195 @@
#pragma once
#include "drape_frontend/gui/shape.hpp"
#include "base/buffer_vector.hpp"
#include "drape/binding_info.hpp"
#include "drape/drape_global.hpp"
#include "drape/glsl_types.hpp"
#include "drape/texture_manager.hpp"
#include <functional>
#include <string>
#include <unordered_set>
namespace gui
{
using TAlphabet = std::unordered_set<strings::UniChar>;
class StaticLabel
{
public:
struct Vertex
{
Vertex() = default;
Vertex(glsl::vec3 const & pos, glsl::vec2 const & color, glsl::vec2 const & outline, glsl::vec2 const & normal,
glsl::vec2 const & mask)
: m_position(pos)
, m_colorTexCoord(color)
, m_outlineColorTexCoord(outline)
, m_normal(normal)
, m_maskTexCoord(mask)
{}
static dp::BindingInfo const & GetBindingInfo();
glsl::vec3 m_position;
glsl::vec2 m_colorTexCoord;
glsl::vec2 m_outlineColorTexCoord;
glsl::vec2 m_normal;
glsl::vec2 m_maskTexCoord;
};
struct LabelResult
{
LabelResult();
dp::RenderState m_state;
buffer_vector<Vertex, 128> m_buffer;
m2::RectF m_boundRect;
TAlphabet m_alphabet;
};
static dp::TGlyphs CacheStaticText(std::string const & text, char const * delim, dp::Anchor anchor,
dp::FontDecl const & font, ref_ptr<dp::TextureManager> mng, LabelResult & result);
};
class MutableLabel
{
public:
struct StaticVertex
{
StaticVertex() = default;
StaticVertex(glsl::vec3 const & position, glsl::vec2 const & color, glsl::vec2 const & outlineColor)
: m_position(position)
, m_color(color)
, m_outline(outlineColor)
{}
static dp::BindingInfo const & GetBindingInfo();
glsl::vec3 m_position;
glsl::vec2 m_color;
glsl::vec2 m_outline;
};
struct DynamicVertex
{
DynamicVertex() = default;
DynamicVertex(glsl::vec2 const & normal, glsl::vec2 const & mask) : m_normal(normal), m_maskTexCoord(mask) {}
static dp::BindingInfo const & GetBindingInfo();
glsl::vec2 m_normal;
glsl::vec2 m_maskTexCoord;
};
explicit MutableLabel(dp::Anchor anchor);
struct PrecacheParams
{
std::string m_alphabet;
size_t m_maxLength;
dp::FontDecl m_font;
};
struct PrecacheResult
{
PrecacheResult();
dp::RenderState m_state;
buffer_vector<StaticVertex, 128> m_buffer;
m2::PointF m_maxPixelSize;
};
struct LabelResult
{
buffer_vector<DynamicVertex, 128> m_buffer;
m2::RectF m_boundRect;
};
void Precache(PrecacheParams const & params, PrecacheResult & result, ref_ptr<dp::TextureManager> mng);
void SetText(LabelResult & result, std::string text, ref_ptr<dp::TextureManager> mng);
// TODO(AB): Refactor.
dp::TGlyphs GetGlyphs() const;
private:
void SetMaxLength(uint16_t maxLength);
private:
dp::Anchor m_anchor;
uint16_t m_maxLength = 0;
float m_textRatio = 0.0f;
dp::text::TextMetrics m_shapedText;
dp::TextureManager::TGlyphsBuffer m_glyphRegions;
};
class MutableLabelHandle : public Handle
{
using TBase = Handle;
public:
MutableLabelHandle(uint32_t id, dp::Anchor anchor, m2::PointF const & pivot);
MutableLabelHandle(uint32_t id, dp::Anchor anchor, m2::PointF const & pivot, ref_ptr<dp::TextureManager> textures);
void GetAttributeMutation(ref_ptr<dp::AttributeBufferMutator> mutator) const override;
bool Update(ScreenBase const & screen) override;
ref_ptr<MutableLabel> GetTextView() const;
protected:
void SetContent(std::string && content);
void SetContent(std::string const & content);
void SetTextureManager(ref_ptr<dp::TextureManager> textures);
private:
drape_ptr<MutableLabel> m_textView;
mutable bool m_isContentDirty;
std::string m_content;
ref_ptr<dp::TextureManager> m_textureManager;
bool m_glyphsReady;
};
class MutableLabelDrawer
{
public:
using TCreatoreResult = drape_ptr<MutableLabelHandle>;
using THandleCreator = std::function<TCreatoreResult(dp::Anchor, m2::PointF const & /* pivot */)>;
struct Params
{
dp::Anchor m_anchor;
dp::FontDecl m_font;
m2::PointF m_pivot;
std::string m_alphabet;
uint32_t m_maxLength;
THandleCreator m_handleCreator;
};
// Return maximum pixel size.
static m2::PointF Draw(ref_ptr<dp::GraphicsContext> context, Params const & params, ref_ptr<dp::TextureManager> mng,
dp::Batcher::TFlushFn && flushFn);
};
class StaticLabelHandle : public Handle
{
using TBase = Handle;
public:
StaticLabelHandle(uint32_t id, ref_ptr<dp::TextureManager> textureManager, dp::Anchor anchor,
m2::PointF const & pivot, dp::TGlyphs && glyphs);
bool Update(ScreenBase const & screen) override;
private:
dp::TGlyphs m_glyphs;
ref_ptr<dp::TextureManager> m_textureManager;
bool m_glyphsReady;
};
} // namespace gui

View file

@ -0,0 +1,378 @@
#include "drape_frontend/gui/layer_render.hpp"
#include "drape_frontend/gui/choose_position_mark.hpp"
#include "drape_frontend/gui/compass.hpp"
#include "drape_frontend/gui/copyright_label.hpp"
#include "drape_frontend/gui/debug_label.hpp"
#include "drape_frontend/gui/drape_gui.hpp"
#include "drape_frontend/gui/gui_text.hpp"
#include "drape_frontend/gui/ruler.hpp"
#include "drape_frontend/gui/ruler_helper.hpp"
#include "drape_frontend/visual_params.hpp"
#include "drape/batcher.hpp"
#include "drape/graphics_context.hpp"
#include "drape/render_bucket.hpp"
#include "geometry/mercator.hpp"
#include "geometry/point2d.hpp"
#include "base/stl_helpers.hpp"
#include <functional>
#include <ios>
#include <sstream>
#include <utility>
using namespace std::placeholders;
namespace gui
{
LayerRenderer::~LayerRenderer()
{
DestroyRenderers();
}
void LayerRenderer::Build(ref_ptr<dp::GraphicsContext> context, ref_ptr<gpu::ProgramManager> mng)
{
for (auto & r : m_renderers)
r.second->Build(context, mng);
}
void LayerRenderer::Render(ref_ptr<dp::GraphicsContext> context, ref_ptr<gpu::ProgramManager> mng, bool routingActive,
ScreenBase const & screen)
{
if (HasWidget(gui::WIDGET_RULER))
{
DrapeGui::GetRulerHelper().ResetTextDirtyFlag();
DrapeGui::GetRulerHelper().Update(screen);
}
for (auto & r : m_renderers)
{
if (routingActive && (r.first == gui::WIDGET_COMPASS || r.first == gui::WIDGET_RULER))
continue;
r.second->Render(context, mng, screen);
}
}
void LayerRenderer::Merge(ref_ptr<LayerRenderer> other)
{
bool activeOverlayFound = false;
for (auto & r : other->m_renderers)
{
auto const it = m_renderers.find(r.first);
if (it != m_renderers.end())
{
auto newActiveOverlay = r.second->FindHandle(m_activeOverlayId);
bool const updateActive = (m_activeOverlay != nullptr && newActiveOverlay != nullptr);
it->second = std::move(r.second);
if (!activeOverlayFound && updateActive)
{
activeOverlayFound = true;
m_activeOverlay = newActiveOverlay;
if (m_activeOverlay != nullptr)
m_activeOverlay->OnTapBegin();
}
}
else
{
m_renderers.insert(std::make_pair(r.first, std::move(r.second)));
}
}
other->m_renderers.clear();
}
void LayerRenderer::SetLayout(TWidgetsLayoutInfo const & info)
{
for (auto const & node : info)
{
auto renderer = m_renderers.find(node.first);
if (renderer != m_renderers.end())
renderer->second->SetPivot(m2::PointF(node.second));
}
}
void LayerRenderer::DestroyRenderers()
{
m_renderers.clear();
}
void LayerRenderer::AddShapeRenderer(EWidget widget, drape_ptr<ShapeRenderer> && shape)
{
if (shape == nullptr)
return;
VERIFY(m_renderers.insert(std::make_pair(widget, std::move(shape))).second, ());
}
bool LayerRenderer::OnTouchDown(m2::RectD const & touchArea)
{
for (auto & r : m_renderers)
{
m_activeOverlay = r.second->ProcessTapEvent(touchArea);
if (m_activeOverlay != nullptr)
{
m_activeOverlayId = m_activeOverlay->GetOverlayID().m_featureId;
m_activeOverlay->OnTapBegin();
return true;
}
}
return false;
}
void LayerRenderer::OnTouchUp(m2::RectD const & touchArea)
{
if (m_activeOverlay != nullptr)
{
if (m_activeOverlay->IsTapped(touchArea))
m_activeOverlay->OnTap();
m_activeOverlay->OnTapEnd();
m_activeOverlay = nullptr;
m_activeOverlayId = FeatureID();
}
}
void LayerRenderer::OnTouchCancel(m2::RectD const & touchArea)
{
UNUSED_VALUE(touchArea);
if (m_activeOverlay != nullptr)
{
m_activeOverlay->OnTapEnd();
m_activeOverlay = nullptr;
m_activeOverlayId = FeatureID();
}
}
bool LayerRenderer::HasWidget(EWidget widget) const
{
return m_renderers.find(widget) != m_renderers.end();
}
namespace
{
class ScaleFpsLabelHandle : public MutableLabelHandle
{
using TBase = MutableLabelHandle;
public:
ScaleFpsLabelHandle(uint32_t id, ref_ptr<dp::TextureManager> textures, std::string const & apiLabel,
Position const & position)
: TBase(id, position.m_anchor, position.m_pixelPivot, textures)
, m_apiLabel(apiLabel)
{
SetIsVisible(true);
}
bool Update(ScreenBase const & screen) override
{
auto & helper = gui::DrapeGui::Instance().GetScaleFpsHelper();
if (!helper.IsVisible())
return false;
if (m_scale != helper.GetScale() || m_fps != helper.GetFps() || m_isPaused != helper.IsPaused())
{
m_scale = helper.GetScale();
m_fps = helper.GetFps();
m_isPaused = helper.IsPaused();
std::stringstream ss;
ss << m_apiLabel << ": Scale: " << m_scale << " / FPS: " << m_fps;
if (m_isPaused)
ss << " (PAUSED)";
SetContent(ss.str());
}
return TBase::Update(screen);
}
private:
std::string const m_apiLabel;
int m_scale = 1;
uint32_t m_fps = 0;
bool m_isPaused = false;
};
} // namespace
drape_ptr<LayerRenderer> LayerCacher::RecacheWidgets(ref_ptr<dp::GraphicsContext> context,
TWidgetsInitInfo const & initInfo,
ref_ptr<dp::TextureManager> textures)
{
using TCacheShape = std::function<void(ref_ptr<dp::GraphicsContext>, Position anchor, ref_ptr<LayerRenderer> renderer,
ref_ptr<dp::TextureManager> textures)>;
static std::map<EWidget, TCacheShape> cacheFunctions{
{WIDGET_COMPASS, std::bind(&LayerCacher::CacheCompass, this, _1, _2, _3, _4)},
{WIDGET_RULER, std::bind(&LayerCacher::CacheRuler, this, _1, _2, _3, _4)},
{WIDGET_COPYRIGHT, std::bind(&LayerCacher::CacheCopyright, this, _1, _2, _3, _4)},
{WIDGET_SCALE_FPS_LABEL, std::bind(&LayerCacher::CacheScaleFpsLabel, this, _1, _2, _3, _4)},
};
drape_ptr<LayerRenderer> renderer = make_unique_dp<LayerRenderer>();
for (auto const & node : initInfo)
{
auto cacheFunction = cacheFunctions.find(node.first);
if (cacheFunction != cacheFunctions.end())
cacheFunction->second(context, node.second, make_ref(renderer), textures);
}
// Flush gui geometry.
context->Flush();
return renderer;
}
drape_ptr<LayerRenderer> LayerCacher::RecacheChoosePositionMark(ref_ptr<dp::GraphicsContext> context,
ref_ptr<dp::TextureManager> textures)
{
m2::PointF const surfSize = DrapeGui::Instance().GetSurfaceSize();
drape_ptr<LayerRenderer> renderer = make_unique_dp<LayerRenderer>();
ChoosePositionMark positionMark = ChoosePositionMark(Position(surfSize * 0.5f, dp::Center));
renderer->AddShapeRenderer(WIDGET_CHOOSE_POSITION_MARK, positionMark.Draw(context, textures));
// Flush gui geometry.
context->Flush();
return renderer;
}
#ifdef RENDER_DEBUG_INFO_LABELS
drape_ptr<LayerRenderer> LayerCacher::RecacheDebugLabels(ref_ptr<dp::GraphicsContext> context,
ref_ptr<dp::TextureManager> textures)
{
drape_ptr<LayerRenderer> renderer = make_unique_dp<LayerRenderer>();
auto const vs = static_cast<float>(df::VisualParams::Instance().GetVisualScale());
DebugInfoLabels debugLabels = DebugInfoLabels(Position(m2::PointF(10.0f * vs, 50.0f * vs), dp::Center));
debugLabels.AddLabel(textures,
"visible: km2, readed: km2, ratio:", [](ScreenBase const & screen, string & content) -> bool
{
double const sizeX = screen.PixelRectIn3d().SizeX();
double const sizeY = screen.PixelRectIn3d().SizeY();
m2::PointD const p0 = screen.PtoG(screen.P3dtoP(m2::PointD(0.0, 0.0)));
m2::PointD const p1 = screen.PtoG(screen.P3dtoP(m2::PointD(0.0, sizeY)));
m2::PointD const p2 = screen.PtoG(screen.P3dtoP(m2::PointD(sizeX, sizeY)));
m2::PointD const p3 = screen.PtoG(screen.P3dtoP(m2::PointD(sizeX, 0.0)));
double const areaG = mercator::AreaOnEarth(p0, p1, p2) + mercator::AreaOnEarth(p2, p3, p0);
double const sizeX_2d = screen.PixelRect().SizeX();
double const sizeY_2d = screen.PixelRect().SizeY();
m2::PointD const p0_2d = screen.PtoG(m2::PointD(0.0, 0.0));
m2::PointD const p1_2d = screen.PtoG(m2::PointD(0.0, sizeY_2d));
m2::PointD const p2_2d = screen.PtoG(m2::PointD(sizeX_2d, sizeY_2d));
m2::PointD const p3_2d = screen.PtoG(m2::PointD(sizeX_2d, 0.0));
double const areaGTotal = mercator::AreaOnEarth(p0_2d, p1_2d, p2_2d) + mercator::AreaOnEarth(p2_2d, p3_2d, p0_2d);
std::ostringstream out;
out << std::fixed << std::setprecision(2) << "visible: " << areaG / 1000000.0 << " km2"
<< ", readed: " << areaGTotal / 1000000.0 << " km2"
<< ", ratio: " << areaGTotal / areaG;
content.assign(out.str());
return true;
});
debugLabels.AddLabel(textures, "scale2d: m/px, scale2d * vs: m/px",
[](ScreenBase const & screen, string & content) -> bool
{
double const distanceG = mercator::DistanceOnEarth(screen.PtoG(screen.PixelRect().LeftBottom()),
screen.PtoG(screen.PixelRect().RightBottom()));
double const vs = df::VisualParams::Instance().GetVisualScale();
double const scale = distanceG / screen.PixelRect().SizeX();
std::ostringstream out;
out << std::fixed << std::setprecision(2) << "scale2d: " << scale << " m/px"
<< ", scale2d * vs: " << scale * vs << " m/px";
content.assign(out.str());
return true;
});
debugLabels.AddLabel(textures, "distance: m", [](ScreenBase const & screen, string & content) -> bool
{
double const sizeX = screen.PixelRectIn3d().SizeX();
double const sizeY = screen.PixelRectIn3d().SizeY();
double const distance = mercator::DistanceOnEarth(screen.PtoG(screen.P3dtoP(m2::PointD(sizeX / 2.0, 0.0))),
screen.PtoG(screen.P3dtoP(m2::PointD(sizeX / 2.0, sizeY))));
std::ostringstream out;
out << std::fixed << std::setprecision(2) << "distance: " << distance << " m";
content.assign(out.str());
return true;
});
debugLabels.AddLabel(textures, "angle: ", [](ScreenBase const & screen, string & content) -> bool
{
std::ostringstream out;
out << std::fixed << std::setprecision(2) << "angle: " << screen.GetRotationAngle() * 180.0 / math::pi;
content.assign(out.str());
return true;
});
renderer->AddShapeRenderer(WIDGET_DEBUG_INFO, debugLabels.Draw(context, textures));
// Flush gui geometry.
context->Flush();
return renderer;
}
#endif
void LayerCacher::CacheCompass(ref_ptr<dp::GraphicsContext> context, Position const & position,
ref_ptr<LayerRenderer> renderer, ref_ptr<dp::TextureManager> textures)
{
Compass compass = Compass(position);
drape_ptr<ShapeRenderer> shape =
compass.Draw(context, textures, std::bind(&DrapeGui::CallOnCompassTappedHandler, &DrapeGui::Instance()));
renderer->AddShapeRenderer(WIDGET_COMPASS, std::move(shape));
}
void LayerCacher::CacheRuler(ref_ptr<dp::GraphicsContext> context, Position const & position,
ref_ptr<LayerRenderer> renderer, ref_ptr<dp::TextureManager> textures)
{
renderer->AddShapeRenderer(WIDGET_RULER, Ruler(position).Draw(context, textures));
}
void LayerCacher::CacheCopyright(ref_ptr<dp::GraphicsContext> context, Position const & position,
ref_ptr<LayerRenderer> renderer, ref_ptr<dp::TextureManager> textures)
{
renderer->AddShapeRenderer(WIDGET_COPYRIGHT, CopyrightLabel(position).Draw(context, textures));
}
void LayerCacher::CacheScaleFpsLabel(ref_ptr<dp::GraphicsContext> context, Position const & position,
ref_ptr<LayerRenderer> renderer, ref_ptr<dp::TextureManager> textures)
{
MutableLabelDrawer::Params params;
params.m_alphabet = "MGLFPSAUEDVcale: 1234567890/()";
params.m_maxLength = 50;
params.m_anchor = position.m_anchor;
params.m_font = DrapeGui::GetGuiTextFont();
params.m_pivot = position.m_pixelPivot;
auto const apiVersion = context->GetApiVersion();
params.m_handleCreator = [textures, apiVersion, &position](dp::Anchor, m2::PointF const &)
{
std::string apiLabel;
switch (apiVersion)
{
case dp::ApiVersion::OpenGLES3: apiLabel = "GL3"; break;
case dp::ApiVersion::Metal: apiLabel = "M"; break;
case dp::ApiVersion::Vulkan: apiLabel = "V"; break;
case dp::ApiVersion::Invalid: CHECK(false, ("Invalid API version.")); break;
}
return make_unique_dp<ScaleFpsLabelHandle>(EGuiHandle::GuiHandleScaleLabel, textures, apiLabel, position);
};
drape_ptr<ShapeRenderer> scaleRenderer = make_unique_dp<ShapeRenderer>();
MutableLabelDrawer::Draw(context, params, textures, std::bind(&ShapeRenderer::AddShape, scaleRenderer.get(), _1, _2));
renderer->AddShapeRenderer(WIDGET_SCALE_FPS_LABEL, std::move(scaleRenderer));
}
} // namespace gui

View file

@ -0,0 +1,82 @@
#pragma once
#include "drape_frontend/gui/shape.hpp"
#include "drape_frontend/gui/skin.hpp"
#include "shaders/program_manager.hpp"
#include "drape/texture_manager.hpp"
#include "geometry/screenbase.hpp"
#include "base/macros.hpp"
#include <map>
namespace dp
{
class GraphicsContext;
} // namespace dp
namespace gui
{
class LayerRenderer
{
public:
LayerRenderer() = default;
~LayerRenderer();
void Build(ref_ptr<dp::GraphicsContext> context, ref_ptr<gpu::ProgramManager> mng);
void Render(ref_ptr<dp::GraphicsContext> context, ref_ptr<gpu::ProgramManager> mng, bool routingActive,
ScreenBase const & screen);
void Merge(ref_ptr<LayerRenderer> other);
void SetLayout(gui::TWidgetsLayoutInfo const & info);
bool OnTouchDown(m2::RectD const & touchArea);
void OnTouchUp(m2::RectD const & touchArea);
void OnTouchCancel(m2::RectD const & touchArea);
bool HasWidget(EWidget widget) const;
private:
void DestroyRenderers();
friend class LayerCacher;
void AddShapeRenderer(EWidget widget, drape_ptr<ShapeRenderer> && shape);
private:
using TRenderers = std::map<EWidget, drape_ptr<ShapeRenderer>>;
TRenderers m_renderers;
ref_ptr<gui::Handle> m_activeOverlay;
FeatureID m_activeOverlayId;
DISALLOW_COPY_AND_MOVE(LayerRenderer);
};
class LayerCacher
{
public:
drape_ptr<LayerRenderer> RecacheWidgets(ref_ptr<dp::GraphicsContext> context, TWidgetsInitInfo const & initInfo,
ref_ptr<dp::TextureManager> textures);
drape_ptr<LayerRenderer> RecacheChoosePositionMark(ref_ptr<dp::GraphicsContext> context,
ref_ptr<dp::TextureManager> textures);
#ifdef RENDER_DEBUG_INFO_LABELS
drape_ptr<LayerRenderer> RecacheDebugLabels(ref_ptr<dp::GraphicsContext> context,
ref_ptr<dp::TextureManager> textures);
#endif
private:
void CacheCompass(ref_ptr<dp::GraphicsContext> context, Position const & position, ref_ptr<LayerRenderer> renderer,
ref_ptr<dp::TextureManager> textures);
void CacheRuler(ref_ptr<dp::GraphicsContext> context, Position const & position, ref_ptr<LayerRenderer> renderer,
ref_ptr<dp::TextureManager> textures);
void CacheCopyright(ref_ptr<dp::GraphicsContext> context, Position const & position, ref_ptr<LayerRenderer> renderer,
ref_ptr<dp::TextureManager> textures);
void CacheScaleFpsLabel(ref_ptr<dp::GraphicsContext> context, Position const & position,
ref_ptr<LayerRenderer> renderer, ref_ptr<dp::TextureManager> textures);
void CacheWatermark(ref_ptr<dp::GraphicsContext> context, Position const & position, ref_ptr<LayerRenderer> renderer,
ref_ptr<dp::TextureManager> textures);
};
} // namespace gui

View file

@ -0,0 +1,243 @@
#include "drape_frontend/gui/ruler.hpp"
#include "drape_frontend/animation/show_hide_animation.hpp"
#include "drape_frontend/batcher_bucket.hpp"
#include "drape_frontend/gui/drape_gui.hpp"
#include "drape_frontend/gui/gui_text.hpp"
#include "drape_frontend/gui/ruler_helper.hpp"
#include "shaders/programs.hpp"
#include "drape/glsl_types.hpp"
#include <functional>
using namespace std::placeholders;
namespace gui
{
namespace
{
struct RulerVertex
{
RulerVertex() = default;
RulerVertex(glsl::vec2 const & pos, glsl::vec2 const & normal, glsl::vec2 const & texCoord)
: m_position(pos)
, m_normal(normal)
, m_texCoord(texCoord)
{}
glsl::vec2 m_position;
glsl::vec2 m_normal;
glsl::vec2 m_texCoord;
};
dp::BindingInfo GetBindingInfo()
{
dp::BindingInfo info(3);
uint8_t offset = 0;
offset += dp::FillDecl<glsl::vec2, RulerVertex>(0, "a_position", info, offset);
offset += dp::FillDecl<glsl::vec2, RulerVertex>(1, "a_normal", info, offset);
/*offset += */ dp::FillDecl<glsl::vec2, RulerVertex>(2, "a_colorTexCoords", info, offset);
return info;
}
template <typename TBase>
class BaseRulerHandle : public TBase
{
public:
BaseRulerHandle(uint32_t id, dp::Anchor anchor, m2::PointF const & pivot, bool isAppearing)
: TBase(id, anchor, pivot)
, m_isAppearing(isAppearing)
, m_isVisibleAtEnd(true)
, m_animation(false, 0.4)
{}
bool Update(ScreenBase const & screen)
{
RulerHelper & helper = DrapeGui::GetRulerHelper();
TBase::SetIsVisible(true);
bool isVisible = RulerHelper::IsVisible(screen);
if (!isVisible)
{
m_animation.HideAnimated();
m_isVisibleAtEnd = false;
}
else if (helper.IsTextDirty())
{
m_isAppearing = !m_isAppearing;
if (m_isAppearing)
{
m_animation.ShowAnimated();
m_isVisibleAtEnd = true;
}
else
{
m_animation.HideAnimated();
m_isVisibleAtEnd = false;
}
}
bool const result = TBase::Update(screen);
UpdateImpl(screen, helper);
if (m_animation.IsFinished())
TBase::SetIsVisible(m_isVisibleAtEnd);
return result;
}
protected:
virtual void UpdateImpl(ScreenBase const & /*screen*/, RulerHelper const & /*helper*/) {}
bool IsAppearing() const { return m_isAppearing; }
float GetOpacity() const { return static_cast<float>(m_animation.GetT()); }
private:
bool m_isAppearing;
bool m_isVisibleAtEnd;
df::ShowHideAnimation m_animation;
};
class RulerHandle : public BaseRulerHandle<Handle>
{
using TBase = BaseRulerHandle<Handle>;
public:
RulerHandle(uint32_t id, dp::Anchor anchor, m2::PointF const & pivot, bool appearing)
: TBase(id, anchor, pivot, appearing)
{}
private:
void UpdateImpl(ScreenBase const & screen, RulerHelper const & helper) override
{
if (!IsVisible())
return;
if (IsAppearing())
m_params.m_length = helper.GetRulerPixelLength();
m_params.m_position = m_pivot;
m_params.m_opacity = GetOpacity();
}
};
class RulerTextHandle : public BaseRulerHandle<MutableLabelHandle>
{
using TBase = BaseRulerHandle<MutableLabelHandle>;
public:
RulerTextHandle(uint32_t id, dp::Anchor anchor, m2::PointF const & pivot, bool isAppearing,
ref_ptr<dp::TextureManager> textures)
: TBase(id, anchor, pivot, isAppearing)
, m_firstUpdate(true)
{
SetTextureManager(textures);
}
bool Update(ScreenBase const & screen) override
{
SetIsVisible(RulerHelper::IsVisible(screen));
if (IsVisible() && (DrapeGui::GetRulerHelper().IsTextDirty() || m_firstUpdate))
{
SetContent(DrapeGui::GetRulerHelper().GetRulerText());
m_firstUpdate = false;
}
return TBase::Update(screen);
}
void SetPivot(glsl::vec2 const & pivot) override
{
TBase::SetPivot(pivot + glsl::vec2(0.0, RulerHelper::GetVerticalTextOffset() - RulerHelper::GetRulerHalfHeight()));
}
protected:
void UpdateImpl(ScreenBase const & /*screen*/, RulerHelper const & /*helper*/) override
{
if (IsVisible())
m_params.m_opacity = GetOpacity();
}
private:
bool m_firstUpdate;
};
} // namespace
drape_ptr<ShapeRenderer> Ruler::Draw(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::TextureManager> tex) const
{
ShapeControl control;
DrawRuler(context, control, tex, true);
DrawRuler(context, control, tex, false);
DrawText(context, control, tex, true);
DrawText(context, control, tex, false);
drape_ptr<ShapeRenderer> renderer = make_unique_dp<ShapeRenderer>();
renderer->AddShapeControl(std::move(control));
return renderer;
}
void Ruler::DrawRuler(ref_ptr<dp::GraphicsContext> context, ShapeControl & control, ref_ptr<dp::TextureManager> tex,
bool isAppearing) const
{
buffer_vector<RulerVertex, 4> data;
dp::TextureManager::ColorRegion reg;
tex->GetColorRegion(DrapeGui::GetGuiTextFont().m_color, reg);
glsl::vec2 texCoord = glsl::ToVec2(reg.GetTexRect().Center());
float const h = RulerHelper::GetRulerHalfHeight();
glsl::vec2 normals[] = {
glsl::vec2(-1.0, 0.0),
glsl::vec2(1.0, 0.0),
};
dp::Anchor anchor = m_position.m_anchor;
if (anchor & dp::Left)
normals[0] = glsl::vec2(0.0, 0.0);
else if (anchor & dp::Right)
normals[1] = glsl::vec2(0.0, 0.0);
data.push_back(RulerVertex(glsl::vec2(0.0, h), normals[0], texCoord));
data.push_back(RulerVertex(glsl::vec2(0.0, -h), normals[0], texCoord));
data.push_back(RulerVertex(glsl::vec2(0.0, h), normals[1], texCoord));
data.push_back(RulerVertex(glsl::vec2(0.0, -h), normals[1], texCoord));
auto state = df::CreateRenderState(gpu::Program::Ruler, df::DepthLayer::GuiLayer);
state.SetColorTexture(reg.GetTexture());
state.SetDepthTestEnabled(false);
dp::AttributeProvider provider(1, 4);
provider.InitStream(0, GetBindingInfo(), make_ref(data.data()));
{
dp::Batcher batcher(dp::Batcher::IndexPerQuad, dp::Batcher::VertexPerQuad);
batcher.SetBatcherHash(static_cast<uint64_t>(df::BatcherBucket::Default));
dp::SessionGuard guard(context, batcher, std::bind(&ShapeControl::AddShape, &control, _1, _2));
batcher.InsertTriangleStrip(context, state, make_ref(&provider),
make_unique_dp<RulerHandle>(EGuiHandle::GuiHandleRuler, m_position.m_anchor,
m_position.m_pixelPivot, isAppearing));
}
}
void Ruler::DrawText(ref_ptr<dp::GraphicsContext> context, ShapeControl & control, ref_ptr<dp::TextureManager> tex,
bool isAppearing) const
{
std::string alphabet;
uint32_t maxTextLength;
RulerHelper::GetTextInitInfo(alphabet, maxTextLength);
MutableLabelDrawer::Params params;
params.m_anchor = static_cast<dp::Anchor>((m_position.m_anchor & (dp::Right | dp::Left)) | dp::Bottom);
params.m_alphabet = alphabet;
params.m_maxLength = maxTextLength;
params.m_font = DrapeGui::GetGuiTextFont();
params.m_pivot = m_position.m_pixelPivot + m2::PointF(0.0f, RulerHelper::GetVerticalTextOffset());
params.m_handleCreator = [isAppearing, tex](dp::Anchor anchor, m2::PointF const & pivot)
{ return make_unique_dp<RulerTextHandle>(EGuiHandle::GuiHandleRulerLabel, anchor, pivot, isAppearing, tex); };
MutableLabelDrawer::Draw(context, params, tex, std::bind(&ShapeControl::AddShape, &control, _1, _2));
}
} // namespace gui

View file

@ -0,0 +1,19 @@
#pragma once
#include "drape_frontend/gui/shape.hpp"
namespace gui
{
class Ruler : public Shape
{
public:
explicit Ruler(Position const & position) : Shape(position) {}
drape_ptr<ShapeRenderer> Draw(ref_ptr<dp::GraphicsContext> context, ref_ptr<dp::TextureManager> tex) const;
private:
void DrawRuler(ref_ptr<dp::GraphicsContext> context, ShapeControl & control, ref_ptr<dp::TextureManager> tex,
bool isAppearing) const;
void DrawText(ref_ptr<dp::GraphicsContext> context, ShapeControl & control, ref_ptr<dp::TextureManager> tex,
bool isAppearing) const;
};
} // namespace gui

View file

@ -0,0 +1,237 @@
#include "drape_frontend/gui/ruler_helper.hpp"
#include "drape_frontend/gui/drape_gui.hpp"
#include "drape_frontend/visual_params.hpp"
#include "platform/measurement_utils.hpp"
#include "geometry/mercator.hpp"
#include "geometry/screenbase.hpp"
#include "base/macros.hpp"
#include <algorithm>
#include <cmath>
#include <iterator>
#include <limits>
#include <set>
namespace gui
{
namespace
{
float constexpr kMinPixelWidth = 60.f;
int constexpr kMinMetersWidth = 5;
int constexpr kMaxMetersWidth = 1000000;
int constexpr kMinUnitValue = -1;
int constexpr kMaxUnitValue = std::numeric_limits<int>::max() - 1;
int constexpr kInvalidUnitValue = kMaxUnitValue + 1;
int constexpr kVisibleRulerBottomScale = 5;
struct UnitValue
{
char const * m_s;
int m_i;
};
// TODO: Localize these strings.
UnitValue constexpr g_arrFeets[] = {
{"10 ft", 10}, {"20 ft", 20}, {"50 ft", 50}, {"100 ft", 100}, {"200 ft", 200},
{"0.1 mi", 528}, {"0.2 mi", 528 * 2}, {"0.5 mi", 528 * 5}, {"1 mi", 5280}, {"2 mi", 2 * 5280},
{"5 mi", 5 * 5280}, {"10 mi", 10 * 5280}, {"20 mi", 20 * 5280}, {"50 mi", 50 * 5280}, {"100 mi", 100 * 5280},
{"200 mi", 200 * 5280}, {"500 mi", 500 * 5280}};
UnitValue constexpr g_arrYards[] = {
{"50 yd", 50}, {"100 yd", 100}, {"200 yd", 200}, {"500 yd", 500}, {"0.5 mi", 1760 / 2},
{"1 mi", 1760}, {"2 mi", 2 * 1760}, {"5 mi", 5 * 1760}, {"10 mi", 10 * 1760}, {"20 mi", 20 * 1760},
{"50 mi", 50 * 1760}, {"100 mi", 100 * 1760}, {"200 mi", 200 * 1760}, {"500 mi", 500 * 1760}};
// TODO: fix ruler text to the current zoom level, i.e. make it 100m for z16 always
// (ruler length will vary still). It'll make debugging and user support easier as
// we'll be able to tell zoom level of a screenshot by looking at the ruler.
UnitValue constexpr g_arrMeters[] = {{"1 m", 1}, {"2 m", 2}, {"5 m", 5}, {"10 m", 10},
{"20 m", 20}, {"50 m", 50}, {"100 m", 100}, {"200 m", 200},
{"500 m", 500}, {"1 km", 1000}, {"2 km", 2000}, {"5 km", 5000},
{"10 km", 10000}, {"20 km", 20000}, {"50 km", 50000}, {"100 km", 100000},
{"200 km", 200000}, {"500 km", 500000}, {"1000 km", 1000000}};
double Identity(double val)
{
return val;
}
} // namespace
RulerHelper::RulerHelper()
: m_pixelLength(0.0f)
, m_rangeIndex(kInvalidUnitValue)
, m_isTextDirty(false)
, m_dirtyTextRequested(false)
{}
void RulerHelper::Update(ScreenBase const & screen)
{
m2::PointD pivot = screen.PixelRect().Center();
long const minPxWidth = std::lround(kMinPixelWidth * df::VisualParams::Instance().GetVisualScale());
m2::PointD pt1 = screen.PtoG(pivot);
m2::PointD pt0 = screen.PtoG(pivot - m2::PointD(minPxWidth, 0));
double const distanceInMeters = mercator::DistanceOnEarth(pt0, pt1);
// convert metres to units for calculating m_metresDiff.
double metersDiff = CalcMetersDiff(distanceInMeters);
bool const higherThanMax = metersDiff > kMaxMetersWidth;
ASSERT_GREATER_OR_EQUAL(metersDiff, kMinMetersWidth, ());
m_pixelLength = static_cast<float>(minPxWidth);
if (higherThanMax)
m_pixelLength = static_cast<float>(minPxWidth) * 3.0f / 2.0f;
else
{
double const a = ang::AngleTo(pt1, pt0);
pt0 = mercator::GetSmPoint(pt1, cos(a) * metersDiff, sin(a) * metersDiff);
m_pixelLength = std::round(pivot.Length(screen.GtoP(pt0)));
}
int drawScale = df::GetDrawTileScale(screen);
if (m_currentDrawScale < kVisibleRulerBottomScale && drawScale >= kVisibleRulerBottomScale)
SetTextDirty();
m_currentDrawScale = drawScale;
}
// static
bool RulerHelper::IsVisible(ScreenBase const & screen)
{
DrapeGui & gui = DrapeGui::Instance();
return !gui.IsCopyrightActive() && df::GetDrawTileScale(screen) >= kVisibleRulerBottomScale;
}
void RulerHelper::Invalidate()
{
SetTextDirty();
}
// static
float RulerHelper::GetRulerHalfHeight()
{
float constexpr kRulerHalfHeight = 1.0f;
return kRulerHalfHeight * static_cast<float>(df::VisualParams::Instance().GetVisualScale());
}
float RulerHelper::GetRulerPixelLength() const
{
return m_pixelLength;
}
// static
float RulerHelper::GetMaxRulerPixelLength()
{
return kMinPixelWidth * 3.0f / 2.0f;
}
// static
int RulerHelper::GetVerticalTextOffset()
{
return static_cast<int>(-5 * df::VisualParams::Instance().GetVisualScale());
}
bool RulerHelper::IsTextDirty() const
{
return m_isTextDirty;
}
std::string const & RulerHelper::GetRulerText() const
{
m_dirtyTextRequested = true;
return m_rulerText;
}
void RulerHelper::ResetTextDirtyFlag()
{
if (m_dirtyTextRequested)
m_isTextDirty = false;
}
// static
void RulerHelper::GetTextInitInfo(std::string & alphabet, uint32_t & size)
{
std::set<char> symbols;
size_t result = 0;
auto const functor = [&result, &symbols](UnitValue const & v)
{
size_t stringSize = strlen(v.m_s);
result = std::max(result, stringSize);
for (size_t i = 0; i < stringSize; ++i)
symbols.insert(v.m_s[i]);
};
std::for_each(std::begin(g_arrFeets), std::end(g_arrFeets), functor);
std::for_each(std::begin(g_arrMeters), std::end(g_arrMeters), functor);
std::for_each(std::begin(g_arrYards), std::end(g_arrYards), functor);
std::for_each(begin(symbols), end(symbols), [&alphabet](char c) { alphabet.push_back(c); });
alphabet.append("<>");
size = static_cast<uint32_t>(result) + 2; // add 2 char for symbols "< " and "> ".
}
double RulerHelper::CalcMetersDiff(double value)
{
UnitValue const * arrU = g_arrMeters;
int count = ARRAY_SIZE(g_arrMeters);
auto conversionFn = &Identity;
using namespace measurement_utils;
if (GetMeasurementUnits() == Units::Imperial)
{
arrU = g_arrFeets;
count = ARRAY_SIZE(g_arrFeets);
conversionFn = &MetersToFeet;
}
int prevUnitRange = m_rangeIndex;
double result = 0.0;
double v = conversionFn(value);
if (arrU[0].m_i > v)
{
m_rangeIndex = kMinUnitValue;
// TODO: "< X" ruler text seems to be never used.
m_rulerText = std::string("< ") + arrU[0].m_s;
result = kMinMetersWidth - 1.0;
}
else if (arrU[count - 1].m_i <= v)
{
m_rangeIndex = kMaxUnitValue;
m_rulerText = std::string("> ") + arrU[count - 1].m_s;
result = kMaxMetersWidth + 1.0;
}
else
{
for (int i = 0; i < count; ++i)
{
if (arrU[i].m_i > v)
{
m_rangeIndex = i;
result = arrU[i].m_i / conversionFn(1.0);
m_rulerText = arrU[i].m_s;
break;
}
}
}
if (m_rangeIndex != prevUnitRange)
SetTextDirty();
return result;
}
void RulerHelper::SetTextDirty()
{
m_dirtyTextRequested = false;
m_isTextDirty = true;
}
} // namespace gui

Some files were not shown because too many files have changed in this diff Show more