diff --git a/drape_frontend/animation/perspective_animation.cpp b/drape_frontend/animation/perspective_animation.cpp new file mode 100644 index 0000000000..d9d4d2c86f --- /dev/null +++ b/drape_frontend/animation/perspective_animation.cpp @@ -0,0 +1,31 @@ +#include "perspective_animation.hpp" +#include "drape_frontend/animation/interpolations.hpp" + +namespace df +{ + +double PerspectiveAnimation::GetRotateDuration(double startAngle, double endAngle) +{ + return 0.5 * fabs(endAngle - startAngle) / math::pi4; +} + +PerspectiveAnimation::PerspectiveAnimation(double duration, double startRotationAngle, double endRotationAngle) + : PerspectiveAnimation(duration, 0.0, startRotationAngle, endRotationAngle) +{ +} + +PerspectiveAnimation::PerspectiveAnimation(double duration, double delay, double startRotationAngle, double endRotationAngle) + : BaseInterpolator(duration, delay) + , m_startRotationAngle(startRotationAngle) + , m_endRotationAngle(endRotationAngle) + , m_rotationAngle(startRotationAngle) +{ +} + +void PerspectiveAnimation::Advance(double elapsedSeconds) +{ + TBase::Advance(elapsedSeconds); + m_rotationAngle = InterpolateDouble(m_startRotationAngle, m_endRotationAngle, GetT()); +} + +} // namespace df diff --git a/drape_frontend/animation/perspective_animation.hpp b/drape_frontend/animation/perspective_animation.hpp new file mode 100644 index 0000000000..d6ef6a6638 --- /dev/null +++ b/drape_frontend/animation/perspective_animation.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include "drape_frontend/animation/base_interpolator.hpp" + +namespace df +{ + +class PerspectiveAnimation : public BaseInterpolator +{ + using TBase = BaseInterpolator; + +public: + static double GetRotateDuration(double startAngle, double endAngle); + + PerspectiveAnimation(double duration, double startRotationAngle, double endRotationAngle); + PerspectiveAnimation(double duration, double delay, double startRotationAngle, double endRotationAngle); + + void Advance(double elapsedSeconds) override; + double GetRotationAngle() const { return m_rotationAngle; } + +private: + double m_startRotationAngle; + double m_endRotationAngle; + double m_rotationAngle; +}; + +} // namespace df + diff --git a/drape_frontend/drape_engine.cpp b/drape_frontend/drape_engine.cpp index 2e65758403..0a8fd8a298 100644 --- a/drape_frontend/drape_engine.cpp +++ b/drape_frontend/drape_engine.cpp @@ -294,11 +294,11 @@ void DrapeEngine::MyPositionNextMode() MessagePriority::High); } -void DrapeEngine::FollowRoute(int preferredZoomLevel) +void DrapeEngine::FollowRoute(int preferredZoomLevel, int preferredZoomLevel3d, double rotationAngle, double angleFOV) { m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread, - make_unique_dp(ChangeMyPositionModeMessage::TYPE_NEXT, - preferredZoomLevel), + make_unique_dp(preferredZoomLevel, preferredZoomLevel3d, + rotationAngle, angleFOV), MessagePriority::High); } @@ -429,17 +429,10 @@ gui::TWidgetsSizeInfo const & DrapeEngine::GetWidgetSizes() return m_widgetSizes; } -void DrapeEngine::Enable3dMode(float rotationAngle, float angleFOV) +void DrapeEngine::Enable3dMode(bool enable) { m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread, - make_unique_dp(rotationAngle, angleFOV), - MessagePriority::Normal); -} - -void DrapeEngine::Disable3dMode() -{ - m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread, - make_unique_dp(), + make_unique_dp(enable), MessagePriority::Normal); } diff --git a/drape_frontend/drape_engine.hpp b/drape_frontend/drape_engine.hpp index b90099f3da..59b00a3aa5 100644 --- a/drape_frontend/drape_engine.hpp +++ b/drape_frontend/drape_engine.hpp @@ -107,15 +107,14 @@ public: void AddRoute(m2::PolylineD const & routePolyline, vector const & turns, dp::Color const & color); void RemoveRoute(bool deactivateFollowing); - void FollowRoute(int preferredZoomLevel); + void FollowRoute(int preferredZoomLevel, int preferredZoomLevel3d, double rotationAngle, double angleFOV); void DeactivateRouteFollowing(); void SetRoutePoint(m2::PointD const & position, bool isStart, bool isValid); void SetWidgetLayout(gui::TWidgetsLayoutInfo && info); gui::TWidgetsSizeInfo const & GetWidgetSizes(); - void Enable3dMode(float rotationAngle, float angleFOV); - void Disable3dMode(); + void Enable3dMode(bool enable); private: void AddUserEvent(UserEvent const & e); diff --git a/drape_frontend/drape_frontend.pro b/drape_frontend/drape_frontend.pro index 2b8aaf6d0e..a832f26b38 100755 --- a/drape_frontend/drape_frontend.pro +++ b/drape_frontend/drape_frontend.pro @@ -17,6 +17,7 @@ SOURCES += \ animation/interpolations.cpp \ animation/model_view_animation.cpp \ animation/opacity_animation.cpp \ + animation/perspective_animation.cpp \ animation/show_hide_animation.cpp \ gui/button.cpp \ gui/compass.cpp \ @@ -96,6 +97,7 @@ HEADERS += \ animation/interpolations.hpp \ animation/model_view_animation.hpp \ animation/opacity_animation.hpp \ + animation/perspective_animation.hpp \ animation/show_hide_animation.hpp \ animation/value_mapping.hpp \ gui/button.hpp \ diff --git a/drape_frontend/frontend_renderer.cpp b/drape_frontend/frontend_renderer.cpp index 138fe83d15..2bd1932537 100755 --- a/drape_frontend/frontend_renderer.cpp +++ b/drape_frontend/frontend_renderer.cpp @@ -45,9 +45,8 @@ FrontendRenderer::FrontendRenderer(Params const & params) , m_gpuProgramManager(new dp::GpuProgramManager()) , m_routeRenderer(new RouteRenderer()) , m_overlayTree(new dp::OverlayTree()) - , m_useFramebuffer(false) + , m_enable3dInNavigation(false) , m_isBillboardRenderPass(false) - , m_3dModeChanged(true) , m_framebuffer(new Framebuffer()) , m_renderer3d(new Renderer3d()) , m_viewport(params.m_viewport) @@ -140,7 +139,8 @@ void FrontendRenderer::AcceptMessage(ref_ptr message) ref_ptr program = m_gpuProgramManager->GetProgram(state.GetProgramIndex()); ref_ptr program3d = m_gpuProgramManager->GetProgram(state.GetProgram3dIndex()); program->Bind(); - bucket->GetBuffer()->Build(m_useFramebuffer ? program3d : program); + bucket->GetBuffer()->Build(m_userEventStream.GetCurrentScreen().isPerspective() ? program3d + : program); if (!IsUserMarkLayer(key)) { if (CheckTileGenerations(key)) @@ -387,7 +387,20 @@ void FrontendRenderer::AcceptMessage(ref_ptr message) ref_ptr msg = message; m_routeRenderer->Clear(); if (msg->NeedDeactivateFollowing()) + { m_myPositionController->DeactivateRouting(); + if (m_enable3dInNavigation) + AddUserEvent(Disable3dModeEvent()); + } + break; + } + + case Message::FollowRoute: + { + ref_ptr msg = message; + m_myPositionController->NextMode(msg->GetPreferredZoomLevel()); + if (m_enable3dInNavigation) + AddUserEvent(Enable3dModeEvent(msg->GetRotationAngle(), msg->GetAngleFOV())); break; } @@ -468,26 +481,11 @@ void FrontendRenderer::AcceptMessage(ref_ptr message) break; } + case Message::Enable3dMode: { - if (!m_useFramebuffer) - { - ref_ptr const msg = message; - AddUserEvent(Enable3dModeEvent(msg->GetRotationAngle(), msg->GetAngleFOV())); - m_useFramebuffer = true; - m_3dModeChanged = true; - } - break; - } - - case Message::Disable3dMode: - { - if (m_useFramebuffer) - { - m_useFramebuffer = false; - m_3dModeChanged = true; - AddUserEvent(Disable3dMode()); - } + ref_ptr const msg = message; + m_enable3dInNavigation = msg->Enable(); break; } @@ -516,17 +514,18 @@ void FrontendRenderer::OnResize(ScreenBase const & screen) m_contextFactory->getDrawContext()->resize(viewportRect.SizeX(), viewportRect.SizeY()); RefreshProjection(); - if (m_useFramebuffer) + if (screen.isPerspective()) { int width = screen.GetWidth(); int height = screen.GetHeight(); int const maxSide = max(width, height); - if (maxSide > m_framebuffer->GetMaxSize()) + int const maxTextureSize = m_framebuffer->GetMaxSize(); + if (maxSide > maxTextureSize) { - width = width * m_framebuffer->GetMaxSize() / maxSide; - height = height * m_framebuffer->GetMaxSize() / maxSide; - LOG(LINFO, ("Max texture size:", m_framebuffer->GetMaxSize(), ", expanded screen size:", maxSide, - ", scale:", m_framebuffer->GetMaxSize() / (double)maxSide)); + width = width * maxTextureSize / maxSide; + height = height * maxTextureSize / maxSide; + LOG(LINFO, ("Max texture size:", maxTextureSize, ", expanded screen size:", maxSide, + ", scale:", maxTextureSize / (double)maxSide)); } m_viewport.SetViewport(0, 0, width, height); @@ -691,7 +690,8 @@ void FrontendRenderer::RenderScene(ScreenBase const & modelView) BeforeDrawFrame(); #endif - if (m_useFramebuffer) + bool const isPerspective = modelView.isPerspective(); + if (isPerspective) { m_framebuffer->SetDefaultContext(m_contextFactory->getDrawContext()); m_framebuffer->Enable(); @@ -784,7 +784,7 @@ void FrontendRenderer::RenderScene(ScreenBase const & modelView) m_routeRenderer->RenderRoute(modelView, make_ref(m_gpuProgramManager), m_generalUniforms); - if (!m_useFramebuffer) + if (!isPerspective) { for (drape_ptr const & group : m_userMarkRenderGroups) { @@ -799,10 +799,10 @@ void FrontendRenderer::RenderScene(ScreenBase const & modelView) m_myPositionController->Render(MyPositionController::RenderMyPosition, modelView, make_ref(m_gpuProgramManager), m_generalUniforms); - if (!m_useFramebuffer && m_guiRenderer != nullptr) + if (!isPerspective && m_guiRenderer != nullptr) m_guiRenderer->Render(make_ref(m_gpuProgramManager), modelView); - if (m_useFramebuffer) + if (isPerspective) { m_framebuffer->Disable(); m_renderer3d->Render(modelView, m_framebuffer->GetTextureId(), make_ref(m_gpuProgramManager)); @@ -842,6 +842,11 @@ void FrontendRenderer::RenderScene(ScreenBase const & modelView) #endif } +bool FrontendRenderer::IsPerspective() const +{ + return m_userEventStream.GetCurrentScreen().isPerspective(); +} + bool FrontendRenderer::IsBillboardProgram(int programIndex) const { return programIndex == gpu::TEXTURING_BILLBOARD_PROGRAM @@ -852,7 +857,7 @@ bool FrontendRenderer::IsBillboardProgram(int programIndex) const void FrontendRenderer::RenderSingleGroup(ScreenBase const & modelView, ref_ptr group) { - if (m_useFramebuffer && + if (modelView.isPerspective() && (m_isBillboardRenderPass != IsBillboardProgram(group->GetState().GetProgram3dIndex()))) return; @@ -885,7 +890,7 @@ void FrontendRenderer::RefreshModelView(ScreenBase const & screen) void FrontendRenderer::RefreshPivotTransform(ScreenBase const & screen) { - if (m_useFramebuffer) + if (screen.isPerspective()) { math::Matrix const transform(screen.PTo3dMatrix()); m_generalUniforms.SetMatrix4x4Value("pivotTransform", transform.m_data); @@ -1228,9 +1233,6 @@ ScreenBase const & FrontendRenderer::ProcessEvents(bool & modelViewChanged, bool ScreenBase const & modelView = m_userEventStream.ProcessEvents(modelViewChanged, viewportChanged); gui::DrapeGui::Instance().SetInUserAction(m_userEventStream.IsInUserAction()); - viewportChanged = viewportChanged || m_3dModeChanged; - m_3dModeChanged = false; - return modelView; } diff --git a/drape_frontend/frontend_renderer.hpp b/drape_frontend/frontend_renderer.hpp index 45e99a27ed..dcb8ed062c 100755 --- a/drape_frontend/frontend_renderer.hpp +++ b/drape_frontend/frontend_renderer.hpp @@ -211,6 +211,7 @@ private: FeatureID GetVisiblePOI(m2::RectD const & pixelRect) const; bool IsBillboardProgram(int programIndex) const; + bool IsPerspective() const; private: drape_ptr m_gpuProgramManager; @@ -229,9 +230,8 @@ private: dp::UniformValuesStorage m_generalUniforms; - bool m_useFramebuffer; + bool m_enable3dInNavigation; bool m_isBillboardRenderPass; - bool m_3dModeChanged; drape_ptr m_framebuffer; drape_ptr m_renderer3d; diff --git a/drape_frontend/message.hpp b/drape_frontend/message.hpp index d050b7ddf9..ce2f50c004 100644 --- a/drape_frontend/message.hpp +++ b/drape_frontend/message.hpp @@ -39,12 +39,12 @@ public: RemoveRoute, FlushRoute, FlushRouteSign, + FollowRoute, DeactivateRouteFollowing, UpdateMapStyle, InvalidateTextures, Invalidate, - Enable3dMode, - Disable3dMode + Enable3dMode }; virtual ~Message() {} diff --git a/drape_frontend/message_subclasses.hpp b/drape_frontend/message_subclasses.hpp index 3e6f3bbbd3..b5ec01dff1 100644 --- a/drape_frontend/message_subclasses.hpp +++ b/drape_frontend/message_subclasses.hpp @@ -604,6 +604,29 @@ public: Type GetType() const override { return Message::UpdateMapStyle; } }; +class FollowRouteMessage : public Message +{ +public: + FollowRouteMessage(int preferredZoomLevel, int preferredZoomLevelIn3d, double rotationAngle, double angleFOV) + : m_preferredZoomLevel(preferredZoomLevel) + , m_preferredZoomLevelIn3d(preferredZoomLevelIn3d) + , m_rotationAngle(rotationAngle) + , m_angleFOV(angleFOV) + {} + + Type GetType() const override { return Message::FollowRoute; } + int GetPreferredZoomLevel() const { return m_preferredZoomLevel; } + int GetPreferredZoomLevelIn3d() const { return m_preferredZoomLevelIn3d; } + double GetRotationAngle() const { return m_rotationAngle; } + double GetAngleFOV() const { return m_angleFOV; } + +private: + int m_preferredZoomLevel; + int m_preferredZoomLevelIn3d; + double m_rotationAngle; + double m_angleFOV; +}; + class InvalidateTexturesMessage : public BaseBlockingMessage { public: @@ -633,28 +656,15 @@ public: class Enable3dModeMessage : public Message { public: - Enable3dModeMessage(float rotationAngle, float angleFOV) - : m_angleFOV(angleFOV) - , m_rotationAngle(rotationAngle) + Enable3dModeMessage(bool enable) + : m_enable(enable) {} Type GetType() const override { return Message::Enable3dMode; } - - float GetAngleFOV() const { return m_angleFOV; } - float GetRotationAngle() const { return m_rotationAngle; } + bool Enable() const { return m_enable; } private: - float m_angleFOV; - float m_rotationAngle; -}; - -class Disable3dModeMessage : public Message -{ -public: - Disable3dModeMessage() - {} - - Type GetType() const override { return Message::Disable3dMode; } + bool m_enable; }; } // namespace df diff --git a/drape_frontend/navigator.cpp b/drape_frontend/navigator.cpp index dcca1010e9..77167048c6 100644 --- a/drape_frontend/navigator.cpp +++ b/drape_frontend/navigator.cpp @@ -31,8 +31,7 @@ namespace df { Navigator::Navigator() - : m_is3dMode(false) - , m_InAction(false) + : m_InAction(false) { } @@ -124,8 +123,8 @@ void Navigator::OnSize(int w, int h) if (fov != 0.0) { - m_Screen.ApplyPerspective(rotation, fov); - m_StartScreen.ApplyPerspective(rotation, fov); + m_Screen.ApplyPerspective(rotation, rotation, fov); + m_StartScreen.ApplyPerspective(rotation, rotation, fov); } } @@ -533,18 +532,21 @@ bool Navigator::IsRotatingDuringScale() const return m_IsRotatingDuringScale; } -void Navigator::Enable3dMode(double rotationAngle, double angleFOV) +void Navigator::Enable3dMode(double currentRotationAngle, double maxRotationAngle, double angleFOV) { - ASSERT(!m_is3dMode, ()); - m_Screen.ApplyPerspective(rotationAngle, angleFOV); - m_is3dMode = true; + ASSERT(!m_Screen.isPerspective(), ()); + m_Screen.ApplyPerspective(currentRotationAngle, maxRotationAngle, angleFOV); +} + +void Navigator::SetRotationIn3dMode(double rotationAngle) +{ + m_Screen.SetRotationAngle(rotationAngle); } void Navigator::Disable3dMode() { - ASSERT(m_is3dMode, ()); + ASSERT(m_Screen.isPerspective(), ()); m_Screen.ResetPerspective(); - m_is3dMode = false; } m2::AnyRectD ToRotated(Navigator const & navigator, m2::RectD const & rect) diff --git a/drape_frontend/navigator.hpp b/drape_frontend/navigator.hpp index 38cf49adee..f61eb89d52 100644 --- a/drape_frontend/navigator.hpp +++ b/drape_frontend/navigator.hpp @@ -47,7 +47,8 @@ public: void CalculateScale(m2::PointD const & pt, double factor, ScreenBase & screen); bool InAction() const; - void Enable3dMode(double rotationAngle, double angleFOV); + void Enable3dMode(double currentRotationAngle, double maxRotationAngle, double angleFOV); + void SetRotationIn3dMode(double rotationAngle); void Disable3dMode(); private: @@ -67,8 +68,6 @@ private: // Internal screen to do GtoP() and PtoG() calculations. It is always up to date with navigation. ScreenBase m_Screen; - bool m_is3dMode; - // Intial point for dragging and scaling. m2::PointD m_StartPt1; // Last point for dragging and scaling. diff --git a/drape_frontend/user_event_stream.cpp b/drape_frontend/user_event_stream.cpp index 3603860d42..0db72657a0 100644 --- a/drape_frontend/user_event_stream.cpp +++ b/drape_frontend/user_event_stream.cpp @@ -134,6 +134,10 @@ ScreenBase const & UserEventStream::ProcessEvents(bool & modelViewChange, bool & swap(m_events, events); } + // Block all events while the perspective animation is playing. + if (m_perspectiveAnimation != nullptr) + events.clear(); + modelViewChange = !events.empty() || m_state != STATE_EMPTY; bool breakAnim = false; for (UserEvent const & e : events) @@ -179,10 +183,10 @@ ScreenBase const & UserEventStream::ProcessEvents(bool & modelViewChange, bool & TouchCancel(m_touches); break; case UserEvent::EVENT_ENABLE_3D_MODE: - Enable3dMode(e.m_enable3dMode.m_rotationAngle, e.m_enable3dMode.m_angleFOV); + m_pendingEnable3dEvent.reset(new Enable3dModeEvent(e.m_enable3dMode)); break; case UserEvent::EVENT_DISABLE_3D_MODE: - Disable3dMode(); + SetDisable3dModeAnimation(); break; default: ASSERT(false, ()); @@ -198,11 +202,39 @@ ScreenBase const & UserEventStream::ProcessEvents(bool & modelViewChange, bool & if (m_animation != nullptr) { - m2::AnyRectD rect = m_animation->GetCurrentRect(GetCurrentScreen()); + m2::AnyRectD const rect = m_animation->GetCurrentRect(GetCurrentScreen()); m_navigator.SetFromRect(rect); modelViewChange = true; if (m_animation->IsFinished()) + { + if (m_animation->GetType() == ModelViewAnimationType::FollowAndRotate && + m_pendingEnable3dEvent != nullptr) + { + SetEnable3dModeAnimation(m_pendingEnable3dEvent->m_rotationAngle); + m_navigator.Enable3dMode(0.0, m_pendingEnable3dEvent->m_rotationAngle, m_pendingEnable3dEvent->m_angleFOV); + viewportChanged = true; + + m_pendingEnable3dEvent.reset(); + } m_animation.reset(); + } + } + + if (m_perspectiveAnimation != nullptr) + { + double const angle = m_perspectiveAnimation->GetRotationAngle(); + m_navigator.SetRotationIn3dMode(angle); + modelViewChange = true; + + if (m_perspectiveAnimation->IsFinished()) + { + if (angle == 0.0) + { + m_navigator.Disable3dMode(); + viewportChanged = true; + } + m_perspectiveAnimation.reset(); + } } if (GetValidTouchesCount(m_touches) == 1) @@ -357,14 +389,23 @@ bool UserEventStream::SetFollowAndRotate(m2::PointD const & userPos, m2::PointD return true; } -void UserEventStream::Enable3dMode(double rotationAngle, double angleFOV) +void UserEventStream::SetEnable3dModeAnimation(double maxRotationAngle) { - m_navigator.Enable3dMode(rotationAngle, angleFOV); + double const startAngle = 0.0; + double const endAngle = maxRotationAngle; + double const rotateDuration = PerspectiveAnimation::GetRotateDuration(startAngle, endAngle); + m_perspectiveAnimation.reset( + new PerspectiveAnimation(rotateDuration, 0.0/*delay*/, startAngle, endAngle)); } -void UserEventStream::Disable3dMode() +void UserEventStream::SetDisable3dModeAnimation() { - m_navigator.Disable3dMode(); + ResetCurrentAnimation(); + + double const startAngle = m_navigator.Screen().GetRotationAngle(); + double const endAngle = 0.0; + double const rotateDuration = PerspectiveAnimation::GetRotateDuration(startAngle, endAngle); + m_perspectiveAnimation.reset(new PerspectiveAnimation(rotateDuration, 0.0/*delay*/, startAngle, endAngle)); } void UserEventStream::ResetCurrentAnimation(bool finishAnimation) diff --git a/drape_frontend/user_event_stream.hpp b/drape_frontend/user_event_stream.hpp index 71b14cd5bb..3c207f0066 100644 --- a/drape_frontend/user_event_stream.hpp +++ b/drape_frontend/user_event_stream.hpp @@ -3,6 +3,7 @@ #include "drape_frontend/kinetic_scroller.hpp" #include "drape_frontend/navigator.hpp" #include "drape_frontend/animation/model_view_animation.hpp" +#include "drape_frontend/animation/perspective_animation.hpp" #include "drape/pointers.hpp" @@ -155,9 +156,9 @@ struct Enable3dModeEvent double m_angleFOV; }; -struct Disable3dMode +struct Disable3dModeEvent { - Disable3dMode() {} + Disable3dModeEvent() {} }; struct RotateEvent @@ -200,7 +201,7 @@ struct UserEvent UserEvent(RotateEvent const & e) : m_type(EVENT_ROTATE) { m_rotate = e; } UserEvent(FollowAndRotateEvent const & e) : m_type(EVENT_FOLLOW_AND_ROTATE) { m_followAndRotate = e; } UserEvent(Enable3dModeEvent const & e) : m_type(EVENT_ENABLE_3D_MODE) { m_enable3dMode = e; } - UserEvent(Disable3dMode const & e) : m_type(EVENT_DISABLE_3D_MODE) { m_disable3dMode = e; } + UserEvent(Disable3dModeEvent const & e) : m_type(EVENT_DISABLE_3D_MODE) { m_disable3dMode = e; } EEventType m_type; union @@ -214,7 +215,7 @@ struct UserEvent RotateEvent m_rotate; FollowAndRotateEvent m_followAndRotate; Enable3dModeEvent m_enable3dMode; - Disable3dMode m_disable3dMode; + Disable3dModeEvent m_disable3dMode; }; }; @@ -284,8 +285,8 @@ private: bool SetFollowAndRotate(m2::PointD const & userPos, m2::PointD const & pixelPos, double azimuth, int preferredZoomLevel, bool isAnim); - void Enable3dMode(double rotationAngle, double angleFOV); - void Disable3dMode(); + void SetEnable3dModeAnimation(double maxRotationAngle); + void SetDisable3dModeAnimation(); m2::AnyRectD GetCurrentRect() const; @@ -345,6 +346,8 @@ private: array m_touches; unique_ptr m_animation; + unique_ptr m_perspectiveAnimation; + unique_ptr m_pendingEnable3dEvent; ref_ptr m_listener; #ifdef DEBUG diff --git a/geometry/screenbase.cpp b/geometry/screenbase.cpp index a948c21428..1ae9dfd428 100644 --- a/geometry/screenbase.cpp +++ b/geometry/screenbase.cpp @@ -17,6 +17,7 @@ ScreenBase::ScreenBase() : m_3dNearZ(0.0), m_3dFarZ(0.0), m_3dAngleX(0.0), + m_3dMaxAngleX(0.0), m_3dScaleX(1.0), m_3dScaleY(1.0), m_isPerspective(false), @@ -254,27 +255,57 @@ void ScreenBase::ExtractGtoPParams(MatrixT const & m, } // Place the camera at the distance, where it gives the same view of plane as the -// orthogonal projection does and rotate the map plane around its near horizontal side. -// Calculate expanded area of visible map plane. -void ScreenBase::ApplyPerspective(double rotationAngle, double angleFOV) +// orthogonal projection does. Calculate the expanded area of the visible map plane +// after rotation through maxRotationAngle around its near horizontal side. +void ScreenBase::ApplyPerspective(double currentRotationAngle, double maxRotationAngle, double angleFOV) { - // TODO: Handle the case when rotationAngle == 0.0. - ASSERT_NOT_EQUAL(rotationAngle, 0.0, ()); ASSERT_NOT_EQUAL(angleFOV, 0.0, ()); + if (m_isPerspective) + ResetPerspective(); + m_isPerspective = true; - m_3dAngleX = rotationAngle; + m_3dMaxAngleX = maxRotationAngle; m_3dFOV = angleFOV; double const halfFOV = m_3dFOV / 2.0; double const cameraZ = 1.0 / tan(halfFOV); // Ratio of the expanded plane's size to the original size. - m_3dScaleY = cos(m_3dAngleX) + sin(m_3dAngleX) * tan(halfFOV + m_3dAngleX); - m_3dScaleX = 1.0 + 2 * sin(m_3dAngleX) * cos(halfFOV) / (cameraZ * cos(halfFOV + m_3dAngleX)); + m_3dScaleY = cos(m_3dMaxAngleX) + sin(m_3dMaxAngleX) * tan(halfFOV + m_3dMaxAngleX); + m_3dScaleX = 1.0 + 2 * sin(m_3dMaxAngleX) * cos(halfFOV) / (cameraZ * cos(halfFOV + m_3dMaxAngleX)); m_3dScaleX = m_3dScaleY = max(m_3dScaleX, m_3dScaleY); + double const dy = m_PixelRect.SizeY() * (m_3dScaleX - 1.0); + + Scale(1.0 / m_3dScaleX); + + m_PixelRect.setMaxX(m_PixelRect.maxX() * m_3dScaleX); + m_PixelRect.setMaxY(m_PixelRect.maxY() * m_3dScaleY); + + Scale(m_3dScaleX); + + Move(0.0, dy / 2.0); + + SetRotationAngle(currentRotationAngle); +} + +// Place the camera at the distance, where it gives the same view of plane as the +// orthogonal projection does and rotate the map plane around its near horizontal side. +void ScreenBase::SetRotationAngle(double rotationAngle) +{ + ASSERT(m_isPerspective, ()); + ASSERT_LESS_OR_EQUAL(rotationAngle, m_3dMaxAngleX, ()); + + if (rotationAngle > m_3dMaxAngleX) + rotationAngle = m_3dMaxAngleX; + + m_3dAngleX = rotationAngle; + + double const halfFOV = m_3dFOV / 2.0; + double const cameraZ = 1.0 / tan(halfFOV); + double const offsetZ = cameraZ + sin(m_3dAngleX) * m_3dScaleY; double const offsetY = cos(m_3dAngleX) * m_3dScaleX - 1.0; @@ -296,38 +327,33 @@ void ScreenBase::ApplyPerspective(double rotationAngle, double angleFOV) m_3dNearZ = cameraZ; m_3dFarZ = cameraZ + 2.0 * sin(m_3dAngleX) * m_3dScaleY; projectionM(0, 0) = projectionM(1, 1) = m_3dNearZ; - projectionM(2, 2) = (m_3dFarZ + m_3dNearZ) / (m_3dFarZ - m_3dNearZ); + projectionM(2, 2) = m_3dAngleX != 0.0 ? (m_3dFarZ + m_3dNearZ) / (m_3dFarZ - m_3dNearZ) + : 0.0; projectionM(2, 3) = 1.0; - projectionM(3, 2) = -2.0 * m_3dFarZ * m_3dNearZ / (m_3dFarZ - m_3dNearZ); + projectionM(3, 2) = m_3dAngleX != 0.0 ? -2.0 * m_3dFarZ * m_3dNearZ / (m_3dFarZ - m_3dNearZ) + : 0.0; m_Pto3d = scaleM * rotateM * translateM * projectionM; m_3dtoP = math::Inverse(m_Pto3d); - - double const dyG = m_GlobalRect.GetLocalRect().SizeY() * (m_3dScaleX - 1.0); - Scale(1.0 / m_3dScaleX); - - MoveG(m2::PointD(0, -dyG / 2.0)); - m_PixelRect.setMaxX(m_PixelRect.maxX() * m_3dScaleX); - m_PixelRect.setMaxY(m_PixelRect.maxY() * m_3dScaleY); - - Scale(m_3dScaleX); } void ScreenBase::ResetPerspective() { m_isPerspective = false; - double const dyG = m_GlobalRect.GetLocalRect().SizeY() * (1.0 - 1.0 / m_3dScaleX); + double const dy = m_PixelRect.SizeY() * (1.0 - 1.0 / m_3dScaleX); Scale(m_3dScaleX); - MoveG(m2::PointD(0, dyG / 2.0)); m_PixelRect.setMaxX(m_PixelRect.maxX() / m_3dScaleX); m_PixelRect.setMaxY(m_PixelRect.maxY() / m_3dScaleY); Scale(1.0 / m_3dScaleX); + Move(0, -dy / 2.0); + m_3dScaleX = m_3dScaleY = 1.0; m_3dAngleX = 0.0; + m_3dMaxAngleX = 0.0; m_3dFOV = 0.0; } @@ -357,13 +383,17 @@ m2::PointD ScreenBase::P3dToP(m2::PointD const & pt) const double const normalizedX = 2.0 * pt.x / PixelRectIn3d().SizeX() - 1.0; double const normalizedY = -2.0 * pt.y / PixelRectIn3d().SizeY() + 1.0; - double const tanX = tan(m_3dAngleX); - double const cameraDistanceZ = - m_3dNearZ * (1.0 + (normalizedY + 1.0) * tanX / (m_3dNearZ - normalizedY * tanX)); + double normalizedZ = 0.0; + if (m_3dAngleX != 0.0) + { + double const tanX = tan(m_3dAngleX); + double const cameraDistanceZ = + m_3dNearZ * (1.0 + (normalizedY + 1.0) * tanX / (m_3dNearZ - normalizedY * tanX)); - double const a = (m_3dFarZ + m_3dNearZ) / (m_3dFarZ - m_3dNearZ); - double const b = -2.0 * m_3dFarZ * m_3dNearZ / (m_3dFarZ - m_3dNearZ); - double const normalizedZ = (a * cameraDistanceZ + b) / cameraDistanceZ; + double const a = (m_3dFarZ + m_3dNearZ) / (m_3dFarZ - m_3dNearZ); + double const b = -2.0 * m_3dFarZ * m_3dNearZ / (m_3dFarZ - m_3dNearZ); + normalizedZ = (a * cameraDistanceZ + b) / cameraDistanceZ; + } Vector3dT const normalizedPoint{normalizedX, normalizedY, normalizedZ, 1.0}; diff --git a/geometry/screenbase.hpp b/geometry/screenbase.hpp index 0a627d6c91..2afdd726a8 100644 --- a/geometry/screenbase.hpp +++ b/geometry/screenbase.hpp @@ -25,6 +25,7 @@ private: double m_3dNearZ; double m_3dFarZ; double m_3dAngleX; + double m_3dMaxAngleX; double m_3dScaleX; double m_3dScaleY; bool m_isPerspective; @@ -125,9 +126,14 @@ public: m2::AnyRectD const & GlobalRect() const { return m_GlobalRect; } m2::RectD const & ClipRect() const { return m_ClipRect; } - void ApplyPerspective(double rotationAngle, double angleFOV); + void ApplyPerspective(double currentRotationAngle, double maxRotationAngle, double angleFOV); void ResetPerspective(); + void SetRotationAngle(double rotationAngle); + double GetRotationAngle() const { return m_3dAngleX; } + + double GetAngleFOV() const { return m_3dFOV; } + m2::PointD P3dToP(m2::PointD const & pt) const; Matrix3dT const & PTo3dMatrix() const { return m_Pto3d; } @@ -139,8 +145,6 @@ public: { return m2::RectD(0.0, 0.0, m_PixelRect.maxX() / m_3dScaleX, m_PixelRect.maxY() / m_3dScaleY); } - double GetRotationAngle() const { return m_3dAngleX; } - double GetAngleFOV() const { return m_3dFOV; } double GetMinPixelRectSize() const; diff --git a/map/framework.cpp b/map/framework.cpp index 5a23560a4b..c7b71e37ff 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -1836,8 +1836,11 @@ void Framework::FollowRoute() int const scale = (m_currentRouterType == RouterType::Pedestrian) ? scales::GetUpperComfortScale() : scales::GetNavigationScale(); - - m_drapeEngine->FollowRoute(scale); + int const scale3d = scale + 1; + double const rotationAngle = math::pi4; + double const angleFOV = math::pi / 3.0; + + m_drapeEngine->FollowRoute(scale, scale3d, rotationAngle, angleFOV); m_drapeEngine->SetRoutePoint(m2::PointD(), true /* isStart */, false /* isValid */); } @@ -2052,8 +2055,5 @@ void Framework::SetRouteFinishPoint(m2::PointD const & pt, bool isValid) void Framework::Enable3dMode(bool enable) { ASSERT(m_drapeEngine != nullptr, ()); - if (enable) - m_drapeEngine->Enable3dMode(M_PI_4, M_PI / 3.0f); - else - m_drapeEngine->Disable3dMode(); + m_drapeEngine->Enable3dMode(enable); }