Repo created
This commit is contained in:
parent
4af19165ec
commit
68073add76
12458 changed files with 12350765 additions and 2 deletions
222
libs/drape_frontend/CMakeLists.txt
Normal file
222
libs/drape_frontend/CMakeLists.txt
Normal 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)
|
||||
132
libs/drape_frontend/animation/animation.cpp
Normal file
132
libs/drape_frontend/animation/animation.cpp
Normal 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
|
||||
169
libs/drape_frontend/animation/animation.hpp
Normal file
169
libs/drape_frontend/animation/animation.hpp
Normal 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
|
||||
185
libs/drape_frontend/animation/arrow_animation.cpp
Normal file
185
libs/drape_frontend/animation/arrow_animation.cpp
Normal 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
|
||||
46
libs/drape_frontend/animation/arrow_animation.hpp
Normal file
46
libs/drape_frontend/animation/arrow_animation.hpp
Normal 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
|
||||
52
libs/drape_frontend/animation/base_interpolator.cpp
Normal file
52
libs/drape_frontend/animation/base_interpolator.cpp
Normal 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
|
||||
24
libs/drape_frontend/animation/base_interpolator.hpp
Normal file
24
libs/drape_frontend/animation/base_interpolator.hpp
Normal 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
|
||||
232
libs/drape_frontend/animation/follow_animation.cpp
Normal file
232
libs/drape_frontend/animation/follow_animation.cpp
Normal 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
|
||||
64
libs/drape_frontend/animation/follow_animation.hpp
Normal file
64
libs/drape_frontend/animation/follow_animation.hpp
Normal 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
|
||||
46
libs/drape_frontend/animation/interpolation_holder.cpp
Normal file
46
libs/drape_frontend/animation/interpolation_holder.cpp
Normal 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
|
||||
31
libs/drape_frontend/animation/interpolation_holder.hpp
Normal file
31
libs/drape_frontend/animation/interpolation_holder.hpp
Normal 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
|
||||
26
libs/drape_frontend/animation/interpolations.cpp
Normal file
26
libs/drape_frontend/animation/interpolations.cpp
Normal 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
|
||||
14
libs/drape_frontend/animation/interpolations.hpp
Normal file
14
libs/drape_frontend/animation/interpolations.hpp
Normal 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
|
||||
279
libs/drape_frontend/animation/interpolators.cpp
Normal file
279
libs/drape_frontend/animation/interpolators.cpp
Normal 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
|
||||
135
libs/drape_frontend/animation/interpolators.hpp
Normal file
135
libs/drape_frontend/animation/interpolators.hpp
Normal 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
|
||||
243
libs/drape_frontend/animation/linear_animation.cpp
Normal file
243
libs/drape_frontend/animation/linear_animation.cpp
Normal 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
|
||||
55
libs/drape_frontend/animation/linear_animation.hpp
Normal file
55
libs/drape_frontend/animation/linear_animation.hpp
Normal 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
|
||||
24
libs/drape_frontend/animation/opacity_animation.cpp
Normal file
24
libs/drape_frontend/animation/opacity_animation.cpp
Normal 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
|
||||
23
libs/drape_frontend/animation/opacity_animation.hpp
Normal file
23
libs/drape_frontend/animation/opacity_animation.hpp
Normal 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
|
||||
206
libs/drape_frontend/animation/parallel_animation.cpp
Normal file
206
libs/drape_frontend/animation/parallel_animation.cpp
Normal 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
|
||||
74
libs/drape_frontend/animation/parallel_animation.hpp
Normal file
74
libs/drape_frontend/animation/parallel_animation.hpp
Normal 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
|
||||
121
libs/drape_frontend/animation/scale_animation.cpp
Normal file
121
libs/drape_frontend/animation/scale_animation.cpp
Normal 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
|
||||
49
libs/drape_frontend/animation/scale_animation.hpp
Normal file
49
libs/drape_frontend/animation/scale_animation.hpp
Normal 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
|
||||
196
libs/drape_frontend/animation/sequence_animation.cpp
Normal file
196
libs/drape_frontend/animation/sequence_animation.cpp
Normal 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
|
||||
59
libs/drape_frontend/animation/sequence_animation.hpp
Normal file
59
libs/drape_frontend/animation/sequence_animation.hpp
Normal 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
|
||||
108
libs/drape_frontend/animation/show_hide_animation.cpp
Normal file
108
libs/drape_frontend/animation/show_hide_animation.cpp
Normal 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
|
||||
39
libs/drape_frontend/animation/show_hide_animation.hpp
Normal file
39
libs/drape_frontend/animation/show_hide_animation.hpp
Normal 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
|
||||
75
libs/drape_frontend/animation/value_mapping.hpp
Normal file
75
libs/drape_frontend/animation/value_mapping.hpp
Normal 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
|
||||
7
libs/drape_frontend/animation_constants.hpp
Normal file
7
libs/drape_frontend/animation_constants.hpp
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
namespace df
|
||||
{
|
||||
double constexpr kMaxAnimationTimeSec = 1.5; // in seconds
|
||||
|
||||
} // namespace df
|
||||
491
libs/drape_frontend/animation_system.cpp
Normal file
491
libs/drape_frontend/animation_system.cpp
Normal 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
|
||||
98
libs/drape_frontend/animation_system.hpp
Normal file
98
libs/drape_frontend/animation_system.hpp
Normal 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
|
||||
15
libs/drape_frontend/animation_utils.cpp
Normal file
15
libs/drape_frontend/animation_utils.cpp
Normal 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
|
||||
10
libs/drape_frontend/animation_utils.hpp
Normal file
10
libs/drape_frontend/animation_utils.hpp
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include "geometry/screenbase.hpp"
|
||||
|
||||
namespace df
|
||||
{
|
||||
|
||||
bool IsAnimationAllowed(double duration, ScreenBase const & screen);
|
||||
|
||||
} // namespace df
|
||||
1208
libs/drape_frontend/apply_feature_functors.cpp
Normal file
1208
libs/drape_frontend/apply_feature_functors.cpp
Normal file
File diff suppressed because it is too large
Load diff
198
libs/drape_frontend/apply_feature_functors.hpp
Normal file
198
libs/drape_frontend/apply_feature_functors.hpp
Normal 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
|
||||
184
libs/drape_frontend/area_shape.cpp
Normal file
184
libs/drape_frontend/area_shape.cpp
Normal 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
|
||||
50
libs/drape_frontend/area_shape.hpp
Normal file
50
libs/drape_frontend/area_shape.hpp
Normal 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
|
||||
502
libs/drape_frontend/arrow3d.cpp
Normal file
502
libs/drape_frontend/arrow3d.cpp
Normal 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
|
||||
113
libs/drape_frontend/arrow3d.hpp
Normal file
113
libs/drape_frontend/arrow3d.hpp
Normal 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
|
||||
809
libs/drape_frontend/backend_renderer.cpp
Normal file
809
libs/drape_frontend/backend_renderer.cpp
Normal 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
|
||||
142
libs/drape_frontend/backend_renderer.hpp
Normal file
142
libs/drape_frontend/backend_renderer.hpp
Normal 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
|
||||
227
libs/drape_frontend/base_renderer.cpp
Normal file
227
libs/drape_frontend/base_renderer.cpp
Normal 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, [¬ified] { 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
|
||||
105
libs/drape_frontend/base_renderer.hpp
Normal file
105
libs/drape_frontend/base_renderer.hpp
Normal 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
|
||||
15
libs/drape_frontend/batcher_bucket.hpp
Normal file
15
libs/drape_frontend/batcher_bucket.hpp
Normal 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
|
||||
82
libs/drape_frontend/batchers_pool.hpp
Normal file
82
libs/drape_frontend/batchers_pool.hpp
Normal 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
|
||||
179
libs/drape_frontend/circles_pack_shape.cpp
Normal file
179
libs/drape_frontend/circles_pack_shape.cpp
Normal 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
|
||||
72
libs/drape_frontend/circles_pack_shape.hpp
Normal file
72
libs/drape_frontend/circles_pack_shape.hpp
Normal 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
|
||||
140
libs/drape_frontend/color_constants.cpp
Normal file
140
libs/drape_frontend/color_constants.cpp
Normal 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
|
||||
22
libs/drape_frontend/color_constants.hpp
Normal file
22
libs/drape_frontend/color_constants.hpp
Normal 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
|
||||
314
libs/drape_frontend/colored_symbol_shape.cpp
Normal file
314
libs/drape_frontend/colored_symbol_shape.cpp
Normal 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
|
||||
33
libs/drape_frontend/colored_symbol_shape.hpp
Normal file
33
libs/drape_frontend/colored_symbol_shape.hpp
Normal 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
|
||||
30
libs/drape_frontend/custom_features_context.hpp
Normal file
30
libs/drape_frontend/custom_features_context.hpp
Normal 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
|
||||
130
libs/drape_frontend/debug_rect_renderer.cpp
Normal file
130
libs/drape_frontend/debug_rect_renderer.cpp
Normal 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
|
||||
50
libs/drape_frontend/debug_rect_renderer.hpp
Normal file
50
libs/drape_frontend/debug_rect_renderer.hpp
Normal 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
|
||||
73
libs/drape_frontend/drape_api.cpp
Normal file
73
libs/drape_frontend/drape_api.cpp
Normal 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
|
||||
70
libs/drape_frontend/drape_api.hpp
Normal file
70
libs/drape_frontend/drape_api.hpp
Normal 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
|
||||
125
libs/drape_frontend/drape_api_builder.cpp
Normal file
125
libs/drape_frontend/drape_api_builder.cpp
Normal 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
|
||||
28
libs/drape_frontend/drape_api_builder.hpp
Normal file
28
libs/drape_frontend/drape_api_builder.hpp
Normal 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
|
||||
82
libs/drape_frontend/drape_api_renderer.cpp
Normal file
82
libs/drape_frontend/drape_api_renderer.cpp
Normal 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
|
||||
31
libs/drape_frontend/drape_api_renderer.hpp
Normal file
31
libs/drape_frontend/drape_api_renderer.hpp
Normal 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
|
||||
918
libs/drape_frontend/drape_engine.cpp
Normal file
918
libs/drape_frontend/drape_engine.cpp
Normal 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
|
||||
290
libs/drape_frontend/drape_engine.hpp
Normal file
290
libs/drape_frontend/drape_engine.hpp
Normal 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
|
||||
47
libs/drape_frontend/drape_engine_params.hpp
Normal file
47
libs/drape_frontend/drape_engine_params.hpp
Normal 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
|
||||
68
libs/drape_frontend/drape_engine_safe_ptr.hpp
Normal file
68
libs/drape_frontend/drape_engine_safe_ptr.hpp
Normal 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
|
||||
13
libs/drape_frontend/drape_frontend_tests/CMakeLists.txt
Normal file
13
libs/drape_frontend/drape_frontend_tests/CMakeLists.txt
Normal 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)
|
||||
|
|
@ -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, ());
|
||||
}
|
||||
84
libs/drape_frontend/drape_frontend_tests/navigator_test.cpp
Normal file
84
libs/drape_frontend/drape_frontend_tests/navigator_test.cpp
Normal 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);
|
||||
}
|
||||
58
libs/drape_frontend/drape_frontend_tests/path_text_test.cpp
Normal file
58
libs/drape_frontend/drape_frontend_tests/path_text_test.cpp
Normal 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, ());
|
||||
}
|
||||
24
libs/drape_frontend/drape_frontend_tests/stylist_tests.cpp
Normal file
24
libs/drape_frontend/drape_frontend_tests/stylist_tests.cpp
Normal 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"})), ());
|
||||
}
|
||||
|
|
@ -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
|
||||
11
libs/drape_frontend/drape_hints.hpp
Normal file
11
libs/drape_frontend/drape_hints.hpp
Normal 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
|
||||
528
libs/drape_frontend/drape_measurer.cpp
Normal file
528
libs/drape_frontend/drape_measurer.cpp
Normal 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
|
||||
202
libs/drape_frontend/drape_measurer.hpp
Normal file
202
libs/drape_frontend/drape_measurer.hpp
Normal 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
|
||||
34
libs/drape_frontend/drape_notifier.cpp
Normal file
34
libs/drape_frontend/drape_notifier.cpp
Normal 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
|
||||
33
libs/drape_frontend/drape_notifier.hpp
Normal file
33
libs/drape_frontend/drape_notifier.hpp
Normal 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
|
||||
66
libs/drape_frontend/engine_context.cpp
Normal file
66
libs/drape_frontend/engine_context.cpp
Normal 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
|
||||
59
libs/drape_frontend/engine_context.hpp
Normal file
59
libs/drape_frontend/engine_context.hpp
Normal 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
|
||||
52
libs/drape_frontend/frame_values.hpp
Normal file
52
libs/drape_frontend/frame_values.hpp
Normal 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
|
||||
2683
libs/drape_frontend/frontend_renderer.cpp
Normal file
2683
libs/drape_frontend/frontend_renderer.cpp
Normal file
File diff suppressed because it is too large
Load diff
452
libs/drape_frontend/frontend_renderer.hpp
Normal file
452
libs/drape_frontend/frontend_renderer.hpp
Normal 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
|
||||
21
libs/drape_frontend/gps_track_point.hpp
Normal file
21
libs/drape_frontend/gps_track_point.hpp
Normal 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
|
||||
316
libs/drape_frontend/gps_track_renderer.cpp
Normal file
316
libs/drape_frontend/gps_track_renderer.cpp
Normal 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
|
||||
55
libs/drape_frontend/gps_track_renderer.hpp
Normal file
55
libs/drape_frontend/gps_track_renderer.hpp
Normal 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
|
||||
98
libs/drape_frontend/gui/choose_position_mark.cpp
Normal file
98
libs/drape_frontend/gui/choose_position_mark.cpp
Normal 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
|
||||
14
libs/drape_frontend/gui/choose_position_mark.hpp
Normal file
14
libs/drape_frontend/gui/choose_position_mark.hpp
Normal 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
|
||||
141
libs/drape_frontend/gui/compass.cpp
Normal file
141
libs/drape_frontend/gui/compass.cpp
Normal 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
|
||||
15
libs/drape_frontend/gui/compass.hpp
Normal file
15
libs/drape_frontend/gui/compass.hpp
Normal 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
|
||||
92
libs/drape_frontend/gui/copyright_label.cpp
Normal file
92
libs/drape_frontend/gui/copyright_label.cpp
Normal 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
|
||||
15
libs/drape_frontend/gui/copyright_label.hpp
Normal file
15
libs/drape_frontend/gui/copyright_label.hpp
Normal 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
|
||||
96
libs/drape_frontend/gui/debug_label.cpp
Normal file
96
libs/drape_frontend/gui/debug_label.cpp
Normal 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
|
||||
26
libs/drape_frontend/gui/debug_label.hpp
Normal file
26
libs/drape_frontend/gui/debug_label.hpp
Normal 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
|
||||
73
libs/drape_frontend/gui/drape_gui.cpp
Normal file
73
libs/drape_frontend/gui/drape_gui.cpp
Normal 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
|
||||
59
libs/drape_frontend/gui/drape_gui.hpp
Normal file
59
libs/drape_frontend/gui/drape_gui.hpp
Normal 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
|
||||
565
libs/drape_frontend/gui/gui_text.cpp
Normal file
565
libs/drape_frontend/gui/gui_text.cpp
Normal 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
|
||||
195
libs/drape_frontend/gui/gui_text.hpp
Normal file
195
libs/drape_frontend/gui/gui_text.hpp
Normal 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
|
||||
378
libs/drape_frontend/gui/layer_render.cpp
Normal file
378
libs/drape_frontend/gui/layer_render.cpp
Normal 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
|
||||
82
libs/drape_frontend/gui/layer_render.hpp
Normal file
82
libs/drape_frontend/gui/layer_render.hpp
Normal 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
|
||||
243
libs/drape_frontend/gui/ruler.cpp
Normal file
243
libs/drape_frontend/gui/ruler.cpp
Normal 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
|
||||
19
libs/drape_frontend/gui/ruler.hpp
Normal file
19
libs/drape_frontend/gui/ruler.hpp
Normal 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
|
||||
237
libs/drape_frontend/gui/ruler_helper.cpp
Normal file
237
libs/drape_frontend/gui/ruler_helper.cpp
Normal 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
Loading…
Add table
Add a link
Reference in a new issue