diff --git a/android/jni/com/mapswithme/maps/Framework.cpp b/android/jni/com/mapswithme/maps/Framework.cpp index fe8bc1cdee..2717980875 100644 --- a/android/jni/com/mapswithme/maps/Framework.cpp +++ b/android/jni/com/mapswithme/maps/Framework.cpp @@ -131,12 +131,11 @@ bool Framework::CreateDrapeEngine(JNIEnv * env, jobject jSurface, int densityDpi m_work.CreateDrapeEngine(make_ref(m_contextFactory), move(p)); m_work.EnterForeground(); - // Load initial state of the map or execute drape tasks which set up custom state. + // Load initial state of the map and execute drape tasks which set up custom state. + LoadState(); { lock_guard lock(m_drapeQueueMutex); - if (m_drapeTasksQueue.empty()) - LoadState(); - else + if (!m_drapeTasksQueue.empty()) ExecuteDrapeTasks(); } @@ -188,6 +187,21 @@ MapStyle Framework::GetMapStyle() const return m_work.GetMapStyle(); } +void Framework::Save3dMode(bool allow3d, bool allow3dBuildings) +{ + m_work.Save3dMode(allow3d, allow3dBuildings); +} + +void Framework::Set3dMode(bool allow3d, bool allow3dBuildings) +{ + m_work.Allow3dMode(allow3d, allow3dBuildings); +} + +void Framework::Get3dMode(bool & allow3d, bool & allow3dBuildings) +{ + m_work.Load3dMode(allow3d, allow3dBuildings); +} + Storage & Framework::Storage() { return m_work.Storage(); @@ -1312,4 +1326,33 @@ extern "C" { frm()->DeregisterAllMaps(); } + + JNIEXPORT void JNICALL + Java_com_mapswithme_maps_Framework_nativeSet3dMode(JNIEnv * env, jclass thiz, jboolean allow, jboolean allowBuildings) + { + bool const allow3d = static_cast(allow); + bool const allow3dBuildings = static_cast(allowBuildings); + + g_framework->Save3dMode(allow3d, allow3dBuildings); + g_framework->PostDrapeTask([allow3d, allow3dBuildings]() + { + g_framework->Set3dMode(allow3d, allow3dBuildings); + }); + } + + JNIEXPORT void JNICALL + Java_com_mapswithme_maps_Framework_nativeGet3dMode(JNIEnv * env, jclass thiz, jobject result) + { + bool enabled; + bool buildings; + g_framework->Get3dMode(enabled, buildings); + + jclass const resultClass = env->GetObjectClass(result); + + static jfieldID const enabledField = env->GetFieldID(resultClass, "enabled", "Z"); + env->SetBooleanField(result, enabledField, enabled); + + static jfieldID const buildingsField = env->GetFieldID(resultClass, "buildings", "Z"); + env->SetBooleanField(result, buildingsField, buildings); + } } // extern "C" diff --git a/android/jni/com/mapswithme/maps/Framework.hpp b/android/jni/com/mapswithme/maps/Framework.hpp index 7d1ffe9700..154da0cf6d 100644 --- a/android/jni/com/mapswithme/maps/Framework.hpp +++ b/android/jni/com/mapswithme/maps/Framework.hpp @@ -156,6 +156,10 @@ namespace android location::EMyPositionMode GetMyPositionMode() const; void SetMyPositionMode(location::EMyPositionMode mode); + void Save3dMode(bool allow3d, bool allow3dBuildings); + void Set3dMode(bool allow3d, bool allow3dBuildings); + void Get3dMode(bool & allow3d, bool & allow3dBuildings); + void SetupWidget(gui::EWidget widget, float x, float y, dp::Anchor anchor); void ApplyWidgets(); void CleanWidgets(); diff --git a/android/res/values/donottranslate.xml b/android/res/values/donottranslate.xml index 4f98701e31..8ec810903d 100644 --- a/android/res/values/donottranslate.xml +++ b/android/res/values/donottranslate.xml @@ -40,6 +40,8 @@ MapStyle TtsEnabled TtsLanguage + 3D + 3DBuildings DisplayShowcase %1$s: %2$s diff --git a/android/res/xml-v21/prefs_route.xml b/android/res/xml-v21/prefs_route.xml index 10ec5db366..a7512432f4 100644 --- a/android/res/xml-v21/prefs_route.xml +++ b/android/res/xml-v21/prefs_route.xml @@ -11,4 +11,16 @@ + + + + \ No newline at end of file diff --git a/android/res/xml/prefs_route.xml b/android/res/xml/prefs_route.xml index a604318998..76399eb2a8 100644 --- a/android/res/xml/prefs_route.xml +++ b/android/res/xml/prefs_route.xml @@ -9,4 +9,12 @@ + + + + \ No newline at end of file diff --git a/android/src/com/mapswithme/maps/Framework.java b/android/src/com/mapswithme/maps/Framework.java index 5cd6e3ce8e..e0e63afe95 100644 --- a/android/src/com/mapswithme/maps/Framework.java +++ b/android/src/com/mapswithme/maps/Framework.java @@ -48,6 +48,12 @@ public class Framework void onRouteBuildingProgress(float progress); } + public static class Params3dMode + { + public boolean enabled; + public boolean buildings; + } + // this class is just bridge between Java and C++ worlds, we must not create it private Framework() {} @@ -177,4 +183,8 @@ public class Framework public native static void nativeRegisterMaps(); public native static void nativeDeregisterMaps(); + + public native static void nativeGet3dMode(Params3dMode result); + + public native static void nativeSet3dMode(boolean allow3d, boolean allow3dBuildings); } diff --git a/android/src/com/mapswithme/maps/settings/RoutePrefsFragment.java b/android/src/com/mapswithme/maps/settings/RoutePrefsFragment.java index ed790ac6dc..7f22602722 100644 --- a/android/src/com/mapswithme/maps/settings/RoutePrefsFragment.java +++ b/android/src/com/mapswithme/maps/settings/RoutePrefsFragment.java @@ -13,6 +13,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import com.mapswithme.maps.Framework; import com.mapswithme.maps.R; import com.mapswithme.maps.sound.LanguageData; import com.mapswithme.maps.sound.TtsPlayer; @@ -151,6 +152,34 @@ public class RoutePrefsFragment extends PreferenceFragment mPrefEnabled = (TwoStatePreference) findPreference(getString(R.string.pref_tts_enabled)); mPrefLanguages = (ListPreference) findPreference(getString(R.string.pref_tts_language)); update(); + + Framework.Params3dMode _3d = new Framework.Params3dMode(); + Framework.nativeGet3dMode(_3d); + + final TwoStatePreference pref3d = (TwoStatePreference)findPreference(getString(R.string.pref_3d)); + final TwoStatePreference pref3dBuildings = (TwoStatePreference)findPreference(getString(R.string.pref_3d_buildings)); + pref3d.setChecked(_3d.enabled); + pref3dBuildings.setChecked(_3d.buildings); + + pref3d.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() + { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) + { + Framework.nativeSet3dMode((Boolean)newValue, pref3dBuildings.isChecked()); + return true; + } + }); + + pref3dBuildings.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() + { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) + { + Framework.nativeSet3dMode(pref3d.isChecked(), (Boolean)newValue); + return true; + } + }); } @Override diff --git a/data/drules_proto_clear.bin b/data/drules_proto_clear.bin index 9247804a46..b597f710dd 100644 Binary files a/data/drules_proto_clear.bin and b/data/drules_proto_clear.bin differ diff --git a/data/drules_proto_clear.txt b/data/drules_proto_clear.txt index 70a087e526..f630018d2e 100644 --- a/data/drules_proto_clear.txt +++ b/data/drules_proto_clear.txt @@ -7490,6 +7490,37 @@ cont { } } } +cont { + name: "building:part" + element { + scale: 16 + area { + color: 868862139 + priority: 1298 + } + } + element { + scale: 17 + area { + color: 12763315 + priority: 1298 + } + } + element { + scale: 18 + area { + color: 12763315 + priority: 1298 + } + } + element { + scale: 19 + area { + color: 12763315 + priority: 1298 + } + } +} cont { name: "craft-brewery" element { diff --git a/data/drules_proto_dark.bin b/data/drules_proto_dark.bin index 95f9ee4d32..8b2ac79ab5 100644 Binary files a/data/drules_proto_dark.bin and b/data/drules_proto_dark.bin differ diff --git a/data/drules_proto_dark.txt b/data/drules_proto_dark.txt index ed6090a9ae..4476fca5bd 100644 --- a/data/drules_proto_dark.txt +++ b/data/drules_proto_dark.txt @@ -7490,6 +7490,37 @@ cont { } } } +cont { + name: "building:part" + element { + scale: 16 + area { + color: 857874978 + priority: 1298 + } + } + element { + scale: 17 + area { + color: 3355443 + priority: 1298 + } + } + element { + scale: 18 + area { + color: 3355443 + priority: 1298 + } + } + element { + scale: 19 + area { + color: 3355443 + priority: 1298 + } + } +} cont { name: "craft-brewery" element { diff --git a/data/styles/clear/include/buildings.mapcss b/data/styles/clear/include/buildings.mapcss index 45b981bcf9..08862daee1 100644 --- a/data/styles/clear/include/buildings.mapcss +++ b/data/styles/clear/include/buildings.mapcss @@ -14,6 +14,7 @@ area|z15[landuse=garages] } area|z16[building], +area|z16[building:part], area|z16[landuse=garages] { fill-color: @building; @@ -22,6 +23,7 @@ area|z16[landuse=garages] } area|z17[building], +area|z17[building:part], area|z17[landuse=garages] { fill-color: @building_dark; @@ -30,6 +32,7 @@ area|z17[landuse=garages] } area|z18-[building], +area|z18-[building:part], area|z18-[landuse=garages] { fill-color: @building_dark; diff --git a/drape/depth_constants.hpp b/drape/depth_constants.hpp index e5e8b7dd14..516f5606ee 100644 --- a/drape/depth_constants.hpp +++ b/drape/depth_constants.hpp @@ -8,8 +8,8 @@ namespace dp namespace depth { -float const POSITION_ACCURACY = minDepth; -float const MY_POSITION_MARK = maxDepth; +float const POSITION_ACCURACY = minDepth + 1.0f; +float const MY_POSITION_MARK = maxDepth - 1.0f; } // namespace depth diff --git a/drape/drape.pro b/drape/drape.pro index ba1f059f15..0e9376ec67 100644 --- a/drape/drape.pro +++ b/drape/drape.pro @@ -12,7 +12,10 @@ include($$DRAPE_DIR/drape_common.pri) SOURCES += glfunctions.cpp OTHER_FILES += \ + shaders/area3d_vertex_shader.vsh \ shaders/area_vertex_shader.vsh \ + shaders/arrow3d_fragment_shader.fsh \ + shaders/arrow3d_vertex_shader.vsh \ shaders/button_fragment_shader.fsh \ shaders/button_vertex_shader.vsh \ shaders/circle_shader.fsh \ @@ -26,16 +29,25 @@ OTHER_FILES += \ shaders/line_vertex_shader.vsh \ shaders/my_position_shader.vsh \ shaders/path_symbol_vertex_shader.vsh \ - shaders/position_accuracy_shader.vsh \ - shaders/solid_color_fragment_shader.fsh \ + shaders/position_accuracy3d_shader.vsh \ shaders/route_arrow_fragment_shader.fsh \ shaders/route_fragment_shader.fsh \ shaders/route_vertex_shader.vsh \ shaders/ruler_vertex_shader.vsh \ shaders/shader_index.txt \ + shaders/solid_color_fragment_shader.fsh \ + shaders/text_billboard_vertex_shader.vsh \ shaders/text_fragment_shader.fsh \ - shaders/texturing_fragment_shader.fsh \ - shaders/texturing_vertex_shader.vsh \ - shaders/user_mark.vsh \ + shaders/text_outlined_billboard_vertex_shader.vsh \ + shaders/text_outlined_gui_vertex_shader.vsh \ shaders/text_outlined_vertex_shader.vsh \ shaders/text_vertex_shader.vsh \ + shaders/texturing3d_fragment_shader.fsh \ + shaders/texturing3d_vertex_shader.vsh \ + shaders/texturing_billboard_vertex_shader.vsh \ + shaders/texturing_fragment_shader.fsh \ + shaders/texturing_vertex_shader.vsh \ + shaders/transparent_layer_fragment_shader.fsh \ + shaders/transparent_layer_vertex_shader.vsh \ + shaders/user_mark.vsh \ + shaders/user_mark_billboard.vsh \ diff --git a/drape/glconstants.cpp b/drape/glconstants.cpp index 201ed342dd..c83610bf51 100644 --- a/drape/glconstants.cpp +++ b/drape/glconstants.cpp @@ -67,6 +67,7 @@ const glConst GLRGB = GL_RGB; const glConst GLAlpha = GL_ALPHA; const glConst GLLuminance = GL_LUMINANCE; const glConst GLAlphaLuminance = GL_LUMINANCE_ALPHA; +const glConst GLDepthComponent = GL_DEPTH_COMPONENT; const glConst GLRGBA8 = GL_RGBA8_OES; const glConst GLRGBA4 = GL_RGBA4_OES; @@ -150,5 +151,13 @@ const glConst GLAlways = GL_ALWAYS; const glConst GLActiveUniforms = GL_ACTIVE_UNIFORMS; const glConst GLLineStrip = GL_LINE_STRIP; +const glConst GLTriangles = GL_TRIANGLES; +const glConst GLTriangleStrip = GL_TRIANGLE_STRIP; + +const glConst GLColorAttachment = GL_COLOR_ATTACHMENT0; +const glConst GLDepthAttachment = GL_DEPTH_ATTACHMENT; +const glConst GLStencilAttachment = GL_STENCIL_ATTACHMENT; + +const glConst GLFramebufferComplete = GL_FRAMEBUFFER_COMPLETE; } // namespace GLConst diff --git a/drape/glconstants.hpp b/drape/glconstants.hpp index 3928a2d6f8..5946778ae3 100644 --- a/drape/glconstants.hpp +++ b/drape/glconstants.hpp @@ -44,6 +44,7 @@ extern const glConst GLRGB; extern const glConst GLAlpha; extern const glConst GLLuminance; extern const glConst GLAlphaLuminance; +extern const glConst GLDepthComponent; /// Texture layout size extern const glConst GLRGBA8; @@ -143,5 +144,15 @@ extern const glConst GLActiveUniforms; /// Draw primitives extern const glConst GLLineStrip; +extern const glConst GLTriangles; +extern const glConst GLTriangleStrip; + +/// Framebuffer attachment points +extern const glConst GLColorAttachment; +extern const glConst GLDepthAttachment; +extern const glConst GLStencilAttachment; + +/// Framebuffer status +extern const glConst GLFramebufferComplete; } // namespace GLConst diff --git a/drape/glfunctions.cpp b/drape/glfunctions.cpp index 0e76a454af..ab85aff281 100644 --- a/drape/glfunctions.cpp +++ b/drape/glfunctions.cpp @@ -41,7 +41,6 @@ typedef void (DP_APIENTRY *TglClearFn)(GLbitfield mask); typedef void (DP_APIENTRY *TglViewportFn)(GLint x, GLint y, GLsizei w, GLsizei h); typedef void (DP_APIENTRY *TglFlushFn)(); -typedef void (DP_APIENTRY *TglBindFramebufferFn)(GLenum target, GLuint id); typedef void (DP_APIENTRY *TglActiveTextureFn)(GLenum texture); typedef void (DP_APIENTRY *TglBlendEquationFn)(GLenum mode); @@ -95,12 +94,17 @@ typedef void (DP_APIENTRY *TglUniform4fFn)(GLint location, GLfloat v1, GLfloat v typedef void (DP_APIENTRY *TglUniform1fvFn)(GLint location, GLsizei count, GLfloat const * value); typedef void (DP_APIENTRY *TglUniformMatrix4fvFn)(GLint location, GLsizei count, GLboolean transpose, GLfloat const * value); +typedef void (DP_APIENTRY *TglGenFramebuffersFn)(GLsizei n, GLuint * framebuffers); +typedef void (DP_APIENTRY *TglDeleteFramebuffersFn)(GLsizei n, GLuint const * framebuffers); +typedef void (DP_APIENTRY *TglBindFramebufferFn)(GLenum target, GLuint id); +typedef void (DP_APIENTRY *TglFramebufferTexture2DFn)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef GLenum(DP_APIENTRY *TglCheckFramebufferStatusFn)(GLenum target); + TglClearColorFn glClearColorFn = nullptr; TglClearFn glClearFn = nullptr; TglViewportFn glViewportFn = nullptr; TglFlushFn glFlushFn = nullptr; -TglBindFramebufferFn glBindFramebufferFn = nullptr; TglActiveTextureFn glActiveTextureFn = nullptr; TglBlendEquationFn glBlendEquationFn = nullptr; @@ -155,6 +159,13 @@ TglUniform4fFn glUniform4fFn = nullptr; TglUniform1fvFn glUniform1fvFn = nullptr; TglUniformMatrix4fvFn glUniformMatrix4fvFn = nullptr; +/// FBO +TglGenFramebuffersFn glGenFramebuffersFn = nullptr; +TglDeleteFramebuffersFn glDeleteFramebuffersFn = nullptr; +TglBindFramebufferFn glBindFramebufferFn = nullptr; +TglFramebufferTexture2DFn glFramebufferTexture2DFn = nullptr; +TglCheckFramebufferStatusFn glCheckFramebufferStatusFn = nullptr; + int const GLCompileStatus = GL_COMPILE_STATUS; int const GLLinkStatus = GL_LINK_STATUS; @@ -389,7 +400,6 @@ void GLFunctions::Init() glViewportFn = &::glViewport; glFlushFn = &::glFlush; - glBindFramebufferFn = LOAD_GL_FUNC(TglBindFramebufferFn, glBindFramebuffer); glActiveTextureFn = LOAD_GL_FUNC(TglActiveTextureFn, glActiveTexture); glBlendEquationFn = LOAD_GL_FUNC(TglBlendEquationFn, glBlendEquation); @@ -444,6 +454,13 @@ void GLFunctions::Init() glUniform1fvFn = LOAD_GL_FUNC(TglUniform1fvFn, glUniform1fv); glUniformMatrix4fvFn = LOAD_GL_FUNC(TglUniformMatrix4fvFn, glUniformMatrix4fv); + + /// FBO + glGenFramebuffersFn = LOAD_GL_FUNC(TglGenFramebuffersFn, glGenFramebuffers); + glDeleteFramebuffersFn = LOAD_GL_FUNC(TglDeleteFramebuffersFn, glDeleteFramebuffers); + glBindFramebufferFn = LOAD_GL_FUNC(TglBindFramebufferFn, glBindFramebuffer); + glFramebufferTexture2DFn = LOAD_GL_FUNC(TglFramebufferTexture2DFn, glFramebufferTexture2D); + glCheckFramebufferStatusFn = LOAD_GL_FUNC(TglCheckFramebufferStatusFn, glCheckFramebufferStatus); } void GLFunctions::AttachCache(thread::id const & threadId) @@ -587,12 +604,6 @@ void GLFunctions::glBlendFunc(glConst srcFactor, glConst dstFactor) GLCHECK(::glBlendFunc(srcFactor, dstFactor)); } -void GLFunctions::glBindFramebuffer(glConst target, uint32_t id) -{ - ASSERT(glBindFramebufferFn != nullptr, ()); - GLCHECK(glBindFramebufferFn(target, id)); -} - uint32_t GLFunctions::glGenVertexArray() { ASSERT(glGenVertexArraysFn != nullptr, ()); @@ -963,6 +974,38 @@ void GLFunctions::glDrawArrays(glConst mode, int32_t first, uint32_t count) GLCHECK(::glDrawArrays(mode, first, count)); } +void GLFunctions::glGenFramebuffer(uint32_t * fbo) +{ + ASSERT(glGenFramebuffersFn != nullptr, ()); + GLCHECK(glGenFramebuffersFn(1, fbo)); +} + +void GLFunctions::glDeleteFramebuffer(uint32_t * fbo) +{ + ASSERT(glDeleteFramebuffersFn != nullptr, ()); + GLCHECK(glDeleteFramebuffersFn(1, fbo)); +} + +void GLFunctions::glFramebufferTexture2D(glConst attachment, glConst texture) +{ + ASSERT(glFramebufferTexture2DFn != nullptr, ()); + GLCHECK(glFramebufferTexture2DFn(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, texture, 0)); +} + +void GLFunctions::glBindFramebuffer(uint32_t fbo) +{ + ASSERT(glBindFramebufferFn != nullptr, ()); + GLCHECK(glBindFramebufferFn(GL_FRAMEBUFFER, fbo)); +} + +uint32_t GLFunctions::glCheckFramebufferStatus() +{ + ASSERT(glCheckFramebufferStatusFn != nullptr, ()); + uint32_t const result = glCheckFramebufferStatusFn(GL_FRAMEBUFFER); + GLCHECKCALL(); + return result; +} + namespace { diff --git a/drape/glfunctions.hpp b/drape/glfunctions.hpp index 96396f8ff4..95c2232d81 100644 --- a/drape/glfunctions.hpp +++ b/drape/glfunctions.hpp @@ -47,8 +47,6 @@ public: static void glBlendEquation(glConst function); static void glBlendFunc(glConst srcFactor, glConst dstFactor); - static void glBindFramebuffer(glConst target, uint32_t id); - /// VAO support static uint32_t glGenVertexArray(); static void glBindVertexArray(uint32_t vao); @@ -133,6 +131,13 @@ public: // Draw support static void glDrawElements(uint32_t sizeOfIndex, uint32_t indexCount, uint32_t startIndex = 0); static void glDrawArrays(glConst mode, int32_t first, uint32_t count); + + // FBO support + static void glGenFramebuffer(uint32_t * fbo); + static void glDeleteFramebuffer(uint32_t * fbo); + static void glBindFramebuffer(uint32_t fbo); + static void glFramebufferTexture2D(glConst attachment, glConst texture); + static uint32_t glCheckFramebufferStatus(); }; void CheckGLError(my::SrcPoint const &src); diff --git a/drape/glstate.cpp b/drape/glstate.cpp index 14724c2f7a..9da2f32773 100644 --- a/drape/glstate.cpp +++ b/drape/glstate.cpp @@ -46,6 +46,7 @@ bool Blending::operator == (Blending const & other) const GLState::GLState(uint32_t gpuProgramIndex, DepthLayer depthLayer) : m_gpuProgramIndex(gpuProgramIndex) + , m_gpuProgram3dIndex(gpuProgramIndex) , m_depthLayer(depthLayer) , m_depthFunction(gl_const::GLLessOrEqual) , m_textureFilter(gl_const::GLLinear) @@ -82,6 +83,8 @@ bool GLState::operator<(GLState const & other) const return m_blending < other.m_blending; if (m_gpuProgramIndex != other.m_gpuProgramIndex) return m_gpuProgramIndex < other.m_gpuProgramIndex; + if (m_gpuProgram3dIndex != other.m_gpuProgram3dIndex) + return m_gpuProgram3dIndex < other.m_gpuProgram3dIndex; if (m_colorTexture != other.m_colorTexture) return m_colorTexture < other.m_colorTexture; @@ -92,6 +95,7 @@ bool GLState::operator==(GLState const & other) const { return m_depthLayer == other.m_depthLayer && m_gpuProgramIndex == other.m_gpuProgramIndex && + m_gpuProgram3dIndex == other.m_gpuProgram3dIndex && m_blending == other.m_blending && m_colorTexture == other.m_colorTexture && m_maskTexture == other.m_maskTexture; diff --git a/drape/glstate.hpp b/drape/glstate.hpp index e5a4e481e5..e06f0013fc 100644 --- a/drape/glstate.hpp +++ b/drape/glstate.hpp @@ -59,6 +59,9 @@ public: int GetProgramIndex() const { return m_gpuProgramIndex; } + void SetProgram3dIndex(uint32_t gpuProgram3dIndex) { m_gpuProgram3dIndex = gpuProgram3dIndex; } + int GetProgram3dIndex() const { return m_gpuProgram3dIndex; } + glConst GetDepthFunction() const; void SetDepthFunction(glConst functionName); @@ -70,6 +73,7 @@ public: private: uint32_t m_gpuProgramIndex; + uint32_t m_gpuProgram3dIndex; DepthLayer m_depthLayer; Blending m_blending; glConst m_depthFunction; diff --git a/drape/overlay_handle.cpp b/drape/overlay_handle.cpp index 5dbf714287..92150dc112 100644 --- a/drape/overlay_handle.cpp +++ b/drape/overlay_handle.cpp @@ -5,6 +5,8 @@ namespace dp { +double const k3dAdditionalExtention = 2.0; + struct OverlayHandle::OffsetNodeFinder { public: @@ -19,12 +21,17 @@ private: uint8_t m_bufferID; }; -OverlayHandle::OverlayHandle(FeatureID const & id, dp::Anchor anchor, uint64_t priority) +OverlayHandle::OverlayHandle(FeatureID const & id, + dp::Anchor anchor, + uint64_t priority, + bool isBillboard) : m_id(id) , m_anchor(anchor) , m_priority(priority) , m_overlayRank(OverlayRank0) - , m_extendingSize(0) + , m_extendingSize(0.0) + , m_pivotZ(0.0) + , m_isBillboard(isBillboard) , m_isVisible(false) { } @@ -41,9 +48,14 @@ void OverlayHandle::SetIsVisible(bool isVisible) m_visibilityTimestamp = steady_clock::now(); } -m2::PointD OverlayHandle::GetPivot(ScreenBase const & screen) const +bool OverlayHandle::IsBillboard() const { - m2::RectD r = GetPixelRect(screen); + return m_isBillboard; +} + +m2::PointD OverlayHandle::GetPivot(ScreenBase const & screen, bool perspective) const +{ + m2::RectD r = GetPixelRect(screen, false); m2::PointD size(0.5 * r.SizeX(), 0.5 * r.SizeY()); m2::PointD result = r.Center(); @@ -57,6 +69,9 @@ m2::PointD OverlayHandle::GetPivot(ScreenBase const & screen) const else if (m_anchor & dp::Bottom) result.y += size.y; + if (perspective) + result = screen.PtoP3d(result, -m_pivotZ / screen.GetScale()); + return result; } @@ -126,16 +141,50 @@ OverlayHandle::TOffsetNode const & OverlayHandle::GetOffsetNode(uint8_t bufferID m2::RectD OverlayHandle::GetExtendedPixelRect(ScreenBase const & screen) const { - m2::RectD rect = GetPixelRect(screen); + m2::RectD rect = GetPixelRect(screen, screen.isPerspective()); rect.Inflate(m_extendingSize, m_extendingSize); + if (Enable3dExtention() && screen.isPerspective()) + rect.Scale(k3dAdditionalExtention); return rect; } void OverlayHandle::GetExtendedPixelShape(ScreenBase const & screen, Rects & rects) const { - GetPixelShape(screen, rects); + GetPixelShape(screen, rects, screen.isPerspective()); for (auto & rect : rects) + { rect.Inflate(m_extendingSize, m_extendingSize); + if (Enable3dExtention() && screen.isPerspective()) + rect.Scale(k3dAdditionalExtention); + } +} + +m2::RectD OverlayHandle::GetPerspectiveRect(m2::RectD const & pixelRect, ScreenBase const & screen) const +{ + m2::PointD const tmpPoint = screen.PtoP3d(pixelRect.LeftTop()); + m2::RectD perspectiveRect(tmpPoint, tmpPoint); + perspectiveRect.Add(screen.PtoP3d(pixelRect.LeftBottom())); + perspectiveRect.Add(screen.PtoP3d(pixelRect.RightBottom())); + perspectiveRect.Add(screen.PtoP3d(pixelRect.RightTop())); + + return perspectiveRect; +} + +m2::RectD OverlayHandle::GetPixelRectPerspective(ScreenBase const & screen) const +{ + if (m_isBillboard) + { + m2::PointD const pxPivot = GetPivot(screen, false); + m2::PointD const pxPivotPerspective = screen.PtoP3d(pxPivot, -m_pivotZ / screen.GetScale()); + + m2::RectD pxRectPerspective = GetPixelRect(screen, false); + pxRectPerspective.Offset(-pxPivot); + pxRectPerspective.Offset(pxPivotPerspective); + + return pxRectPerspective; + } + + return GetPerspectiveRect(GetPixelRect(screen, false), screen); } bool OverlayHandle::IsMinVisibilityTimeUp() const @@ -145,16 +194,26 @@ bool OverlayHandle::IsMinVisibilityTimeUp() const return t > kMinVisibilityTimeMs; } +uint64_t OverlayHandle::GetPriorityInFollowingMode() const +{ + return GetPriority(); +} + + SquareHandle::SquareHandle(FeatureID const & id, dp::Anchor anchor, m2::PointD const & gbPivot, m2::PointD const & pxSize, - uint64_t priority) - : TBase(id, anchor, priority) + uint64_t priority, + bool isBillboard) + : TBase(id, anchor, priority, isBillboard) , m_gbPivot(gbPivot) , m_pxHalfSize(pxSize.x / 2.0, pxSize.y / 2.0) {} -m2::RectD SquareHandle::GetPixelRect(ScreenBase const & screen) const +m2::RectD SquareHandle::GetPixelRect(ScreenBase const & screen, bool perspective) const { + if (perspective) + return GetPixelRectPerspective(screen); + m2::PointD const pxPivot = screen.GtoP(m_gbPivot); m2::RectD result(pxPivot - m_pxHalfSize, pxPivot + m_pxHalfSize); m2::PointD offset(0.0, 0.0); @@ -173,10 +232,9 @@ m2::RectD SquareHandle::GetPixelRect(ScreenBase const & screen) const return result; } -void SquareHandle::GetPixelShape(ScreenBase const & screen, Rects & rects) const +void SquareHandle::GetPixelShape(ScreenBase const & screen, Rects & rects, bool perspective) const { - m2::RectD rd = GetPixelRect(screen); - rects.push_back(m2::RectF(rd.minX(), rd.minY(), rd.maxX(), rd.maxY())); + rects.emplace_back(GetPixelRect(screen, perspective)); } uint64_t CalculateOverlayPriority(int minZoomLevel, uint8_t rank, float depth) diff --git a/drape/overlay_handle.hpp b/drape/overlay_handle.hpp index ad17da2a7e..0da93b7e5f 100644 --- a/drape/overlay_handle.hpp +++ b/drape/overlay_handle.hpp @@ -39,18 +39,27 @@ class OverlayHandle public: typedef vector Rects; - OverlayHandle(FeatureID const & id, dp::Anchor anchor, uint64_t priority); + OverlayHandle(FeatureID const & id, + dp::Anchor anchor, + uint64_t priority, + bool isBillboard); virtual ~OverlayHandle() {} bool IsVisible() const; void SetIsVisible(bool isVisible); - m2::PointD GetPivot(ScreenBase const & screen) const; + bool IsBillboard() const; + + virtual m2::PointD GetPivot(ScreenBase const & screen, bool perspective) const; virtual bool Update(ScreenBase const & /*screen*/) { return true; } - virtual m2::RectD GetPixelRect(ScreenBase const & screen) const = 0; - virtual void GetPixelShape(ScreenBase const & screen, Rects & rects) const = 0; + + virtual m2::RectD GetPixelRect(ScreenBase const & screen, bool perspective) const = 0; + virtual void GetPixelShape(ScreenBase const & screen, Rects & rects, bool perspective) const = 0; + + double GetPivotZ() const { return m_pivotZ; } + void SetPivotZ(double pivotZ) { m_pivotZ = pivotZ; } double GetExtendingSize() const { return m_extendingSize; } void SetExtendingSize(double extendingSize) { m_extendingSize = extendingSize; } @@ -72,9 +81,12 @@ public: uint64_t const & GetPriority() const; virtual uint64_t GetPriorityMask() const { return kPriorityMaskAll; } + virtual uint64_t GetPriorityInFollowingMode() const; virtual bool IsBound() const { return false; } + virtual bool Enable3dExtention() const { return true; } + int GetOverlayRank() const { return m_overlayRank; } void SetOverlayRank(int overlayRank) { m_overlayRank = overlayRank; } @@ -87,13 +99,18 @@ protected: int m_overlayRank; double m_extendingSize; + double m_pivotZ; steady_clock::time_point m_visibilityTimestamp; typedef pair TOffsetNode; TOffsetNode const & GetOffsetNode(uint8_t bufferID) const; + m2::RectD GetPerspectiveRect(m2::RectD const & pixelRect, ScreenBase const & screen) const; + m2::RectD GetPixelRectPerspective(ScreenBase const & screen) const; + private: + bool const m_isBillboard; bool m_isVisible; dp::IndexStorage m_indexes; @@ -119,10 +136,11 @@ public: dp::Anchor anchor, m2::PointD const & gbPivot, m2::PointD const & pxSize, - uint64_t priority); + uint64_t priority, + bool isBillboard = false); - virtual m2::RectD GetPixelRect(ScreenBase const & screen) const; - virtual void GetPixelShape(ScreenBase const & screen, Rects & rects) const; + virtual m2::RectD GetPixelRect(ScreenBase const & screen, bool perspective) const override; + virtual void GetPixelShape(ScreenBase const & screen, Rects & rects, bool perspective) const override; private: m2::PointD m_gbPivot; diff --git a/drape/overlay_tree.cpp b/drape/overlay_tree.cpp index 6296a853bf..6358f62987 100644 --- a/drape/overlay_tree.cpp +++ b/drape/overlay_tree.cpp @@ -7,6 +7,7 @@ namespace dp { int const kFrameUpdarePeriod = 10; +int const kFrameUpdarePeriodIn3d = 30; int const kAverageHandlesCount[dp::OverlayRanksCount] = { 300, 200, 100 }; using TOverlayContainer = buffer_vector; @@ -17,6 +18,8 @@ namespace class HandleComparator { public: + HandleComparator(bool followingMode) : m_followingMode(followingMode) {} + bool operator()(ref_ptr const & l, ref_ptr const & r) const { return IsGreater(l, r); @@ -25,8 +28,10 @@ public: bool IsGreater(ref_ptr const & l, ref_ptr const & r) const { uint64_t const mask = l->GetPriorityMask() & r->GetPriorityMask(); - uint64_t const priorityLeft = l->GetPriority() & mask; - uint64_t const priorityRight = r->GetPriority() & mask; + uint64_t const priorityLeft = (m_followingMode ? l->GetPriorityInFollowingMode() : + l->GetPriority()) & mask; + uint64_t const priorityRight = (m_followingMode ? r->GetPriorityInFollowingMode() : + r->GetPriority()) & mask; if (priorityLeft > priorityRight) return true; @@ -43,24 +48,28 @@ public: return false; } + +private: + bool m_followingMode; }; } // namespace OverlayTree::OverlayTree() : m_frameCounter(-1) + , m_followingMode(false) { for (size_t i = 0; i < m_handles.size(); i++) m_handles[i].reserve(kAverageHandlesCount[i]); } -bool OverlayTree::Frame() +bool OverlayTree::Frame(bool is3d) { if (IsNeedUpdate()) return true; m_frameCounter++; - if (m_frameCounter >= kFrameUpdarePeriod) + if (m_frameCounter >= (is3d ? kFrameUpdarePeriodIn3d : kFrameUpdarePeriod)) m_frameCounter = -1; return IsNeedUpdate(); @@ -88,6 +97,7 @@ void OverlayTree::Add(ref_ptr handle) ASSERT(IsNeedUpdate(), ()); ScreenBase const & modelView = GetModelView(); + bool const is3dMode = modelView.isPerspective(); handle->SetIsVisible(false); @@ -95,7 +105,9 @@ void OverlayTree::Add(ref_ptr handle) return; m2::RectD const pixelRect = handle->GetExtendedPixelRect(modelView); - if (!m_traits.m_modelView.PixelRect().IsIntersect(pixelRect)) + + if (!modelView.PixelRect().IsIntersect(handle->GetPixelRect(modelView, false)) || + (is3dMode && !modelView.PixelRectIn3d().IsIntersect(pixelRect))) { handle->SetIsVisible(false); return; @@ -112,6 +124,8 @@ void OverlayTree::InsertHandle(ref_ptr handle, ASSERT(IsNeedUpdate(), ()); ScreenBase const & modelView = GetModelView(); + bool const is3dMode = modelView.isPerspective(); + m2::RectD const pixelRect = handle->GetExtendedPixelRect(modelView); TOverlayContainer elements; @@ -134,15 +148,20 @@ void OverlayTree::InsertHandle(ref_ptr handle, if (boundToParent) handleToCompare = parentOverlay.m_handle; + double const posY = handleToCompare->GetPivot(modelView, is3dMode).y; // In this loop we decide which element must be visible. // If input element "handle" more priority than all "Intersected elements" // than we remove all "Intersected elements" and insert input element "handle". // But if some of already inserted elements more priority than we don't insert "handle". - HandleComparator comparator; + HandleComparator comparator(m_followingMode); for (auto const & info : elements) { - bool const timeReject = !info.m_handle->IsMinVisibilityTimeUp(); - if (timeReject || comparator.IsGreater(info.m_handle, handleToCompare)) + bool const pathTextComparation = handle->HasDynamicAttributes() || info.m_handle->HasDynamicAttributes(); + bool const rejectByDepth = is3dMode ? !pathTextComparation && + posY > info.m_handle->GetPivot(modelView, is3dMode).y + : false; + bool const rejectByTime = !info.m_handle->IsMinVisibilityTimeUp(); + if (rejectByDepth || rejectByTime || comparator.IsGreater(info.m_handle, handleToCompare)) { // Handle is displaced and bound to its parent, parent will be displaced too. if (boundToParent) @@ -168,7 +187,7 @@ void OverlayTree::EndOverlayPlacing() { ASSERT(IsNeedUpdate(), ()); - HandleComparator comparator; + HandleComparator comparator(m_followingMode); for (int rank = 0; rank < dp::OverlayRanksCount; rank++) { @@ -234,6 +253,22 @@ void OverlayTree::AddHandleToDelete(detail::OverlayInfo const & overlay) } } +void OverlayTree::Select(m2::PointD const & glbPoint, TSelectResult & result) const +{ + ScreenBase const & screen = m_traits.m_modelView; + m2::PointD const pxPoint = screen.GtoP(glbPoint); + + double const kSearchRectHalfSize = 10.0; + m2::RectD rect(pxPoint, pxPoint); + rect.Inflate(kSearchRectHalfSize, kSearchRectHalfSize); + + ForEach([&](detail::OverlayInfo const & info) + { + if (rect.IsPointInside(info.m_handle->GetPivot(screen, false))) + result.push_back(info.m_handle); + }); +} + void OverlayTree::Select(m2::RectD const & rect, TSelectResult & result) const { ScreenBase screen = m_traits.m_modelView; @@ -242,7 +277,7 @@ void OverlayTree::Select(m2::RectD const & rect, TSelectResult & result) const if (info.m_handle->IsVisible() && info.m_handle->GetFeatureID().IsValid()) { OverlayHandle::Rects shape; - info.m_handle->GetPixelShape(screen, shape); + info.m_handle->GetPixelShape(screen, shape, screen.isPerspective()); for (m2::RectF const & rShape : shape) { if (rShape.IsIntersect(m2::RectF(rect))) @@ -255,4 +290,9 @@ void OverlayTree::Select(m2::RectD const & rect, TSelectResult & result) const }); } +void OverlayTree::SetFollowingMode(bool mode) +{ + m_followingMode = mode; +} + } // namespace dp diff --git a/drape/overlay_tree.hpp b/drape/overlay_tree.hpp index 14e4f00b16..f84d4827c0 100644 --- a/drape/overlay_tree.hpp +++ b/drape/overlay_tree.hpp @@ -50,7 +50,7 @@ class OverlayTree : public m4::Tree public: OverlayTree(); - bool Frame(); + bool Frame(bool is3d); bool IsNeedUpdate() const; void ForceUpdate(); @@ -60,6 +60,9 @@ public: using TSelectResult = buffer_vector, 8>; void Select(m2::RectD const & rect, TSelectResult & result) const; + void Select(m2::PointD const & glbPoint, TSelectResult & result) const; + + void SetFollowingMode(bool mode); private: ScreenBase const & GetModelView() const { return m_traits.m_modelView; } @@ -72,6 +75,7 @@ private: int m_frameCounter; array>, dp::OverlayRanksCount> m_handles; vector m_handlesToDelete; + bool m_followingMode; }; } // namespace dp diff --git a/drape/shaders/area3d_vertex_shader.vsh b/drape/shaders/area3d_vertex_shader.vsh new file mode 100644 index 0000000000..81a1fe54d0 --- /dev/null +++ b/drape/shaders/area3d_vertex_shader.vsh @@ -0,0 +1,30 @@ +attribute vec3 a_position; +attribute vec3 a_normal; +attribute vec2 a_colorTexCoords; + +uniform mat4 modelView; +uniform mat4 projection; +uniform mat4 pivotTransform; +uniform float zScale; + +varying vec2 v_colorTexCoords; +varying float v_intensity; + +const vec4 lightDir = vec4(1.0, 0.0, 3.0, 0.0); + +void main(void) +{ + vec4 pos = vec4(a_position, 1.0) * modelView; + + vec4 normal = vec4(a_position + a_normal, 1.0) * modelView; + normal.xyw = (normal * projection).xyw; + normal.z = normal.z * zScale; + + pos.xyw = (pos * projection).xyw; + pos.z = a_position.z * zScale; + + v_intensity = max(0.0, -dot(normalize(lightDir), normalize(normal - pos))); + + gl_Position = pivotTransform * pos; + v_colorTexCoords = a_colorTexCoords; +} diff --git a/drape/shaders/area_vertex_shader.vsh b/drape/shaders/area_vertex_shader.vsh index b698bb7184..e1866d5fae 100644 --- a/drape/shaders/area_vertex_shader.vsh +++ b/drape/shaders/area_vertex_shader.vsh @@ -3,6 +3,7 @@ attribute vec2 a_colorTexCoords; uniform mat4 modelView; uniform mat4 projection; +uniform mat4 pivotTransform; #ifdef ENABLE_VTF uniform sampler2D u_colorTex; @@ -13,7 +14,11 @@ varying vec2 v_colorTexCoords; void main(void) { - gl_Position = vec4(a_position, 1) * modelView * projection; + vec4 pos = vec4(a_position, 1) * modelView * projection; + float w = pos.w; + pos.xyw = (pivotTransform * vec4(pos.xy, 0.0, w)).xyw; + pos.z *= pos.w / w; + gl_Position = pos; #ifdef ENABLE_VTF v_color = texture2D(u_colorTex, a_colorTexCoords); #else diff --git a/drape/shaders/arrow3d_fragment_shader.fsh b/drape/shaders/arrow3d_fragment_shader.fsh new file mode 100644 index 0000000000..bcdff2015e --- /dev/null +++ b/drape/shaders/arrow3d_fragment_shader.fsh @@ -0,0 +1,9 @@ +varying float v_intensity; + +const vec3 color = vec3(0.0, 0.75, 1.0); + +void main() +{ + gl_FragColor.rgb = (v_intensity * 0.4 + 0.6) * color; + gl_FragColor.a = 1.0; +} diff --git a/drape/shaders/arrow3d_vertex_shader.vsh b/drape/shaders/arrow3d_vertex_shader.vsh new file mode 100644 index 0000000000..1fd6741a9c --- /dev/null +++ b/drape/shaders/arrow3d_vertex_shader.vsh @@ -0,0 +1,17 @@ +attribute vec3 a_pos; +attribute vec3 a_normal; + +uniform mat4 m_transform; + +varying float v_intensity; + +const vec4 lightDir = vec4(1.0, 0.0, 3.0, 0.0); + +void main() +{ + vec4 position = m_transform * vec4(a_pos, 1.0); + vec4 normal = m_transform * vec4(a_normal + a_pos, 1.0); + v_intensity = max(0.0, -dot(normalize(lightDir), normalize(normal - position))); + gl_Position = position; +} + diff --git a/drape/shaders/circle_shader.vsh b/drape/shaders/circle_shader.vsh index 8100470d56..23be8dff96 100644 --- a/drape/shaders/circle_shader.vsh +++ b/drape/shaders/circle_shader.vsh @@ -4,6 +4,7 @@ attribute vec2 a_colorTexCoords; uniform mat4 modelView; uniform mat4 projection; +uniform mat4 pivotTransform; varying vec3 v_radius; #ifdef ENABLE_VTF @@ -15,7 +16,11 @@ varying vec2 v_colorTexCoords; void main(void) { - gl_Position = (vec4(a_normal.xy, 0, 0) + vec4(a_position, 1) * modelView) * projection; + vec4 pos = (vec4(a_normal.xy, 0, 0) + vec4(a_position, 1) * modelView) * projection; + float w = pos.w; + pos.xyw = (pivotTransform * vec4(pos.xy, 0.0, w)).xyw; + pos.z *= pos.w / w; + gl_Position = pos; #ifdef ENABLE_VTF v_color = texture2D(u_colorTex, a_colorTexCoords); #else diff --git a/drape/shaders/dashed_vertex_shader.vsh b/drape/shaders/dashed_vertex_shader.vsh index 411224f0fe..5704469871 100644 --- a/drape/shaders/dashed_vertex_shader.vsh +++ b/drape/shaders/dashed_vertex_shader.vsh @@ -5,6 +5,7 @@ attribute vec4 a_maskTexCoord; uniform mat4 modelView; uniform mat4 projection; +uniform mat4 pivotTransform; varying vec2 v_colorTexCoord; varying vec2 v_maskTexCoord; @@ -26,5 +27,9 @@ void main(void) v_colorTexCoord = a_colorTexCoord; v_maskTexCoord = vec2(a_maskTexCoord.y + uOffset * a_maskTexCoord.z, a_maskTexCoord.w); v_halfLength = vec2(sign(a_normal.z) * halfWidth, abs(a_normal.z)); - gl_Position = vec4(transformedAxisPos, a_position.z, 1.0) * projection; + vec4 pos = vec4(transformedAxisPos, a_position.z, 1.0) * projection; + float w = pos.w; + pos.xyw = (pivotTransform * vec4(pos.xy, 0.0, w)).xyw; + pos.z *= pos.w / w; + gl_Position = pos; } diff --git a/drape/shaders/line_vertex_shader.vsh b/drape/shaders/line_vertex_shader.vsh index 4da6b51551..9aa9ea763c 100644 --- a/drape/shaders/line_vertex_shader.vsh +++ b/drape/shaders/line_vertex_shader.vsh @@ -4,6 +4,7 @@ attribute vec2 a_colorTexCoord; uniform mat4 modelView; uniform mat4 projection; +uniform mat4 pivotTransform; #ifdef ENABLE_VTF uniform sampler2D u_colorTex; @@ -32,5 +33,9 @@ void main(void) v_colorTexCoord = a_colorTexCoord; #endif v_halfLength = vec2(sign(a_normal.z) * halfWidth, abs(a_normal.z)); - gl_Position = vec4(transformedAxisPos, a_position.z, 1.0) * projection; + vec4 pos = vec4(transformedAxisPos, a_position.z, 1.0) * projection; + float w = pos.w; + pos.xyw = (pivotTransform * vec4(pos.xy, 0.0, w)).xyw; + pos.z *= pos.w / w; + gl_Position = pos; } diff --git a/drape/shaders/my_position_shader.vsh b/drape/shaders/my_position_shader.vsh index 191d711799..eabd2537f0 100644 --- a/drape/shaders/my_position_shader.vsh +++ b/drape/shaders/my_position_shader.vsh @@ -6,6 +6,7 @@ uniform float u_azimut; uniform mat4 modelView; uniform mat4 projection; +uniform mat4 pivotTransform; varying vec2 v_colorTexCoords; @@ -24,6 +25,10 @@ void main(void) highp vec4 normal = vec4(a_normal, 0, 0); highp vec4 shiftedPos = normal * rotation + pos; - gl_Position = shiftedPos * projection; + shiftedPos = shiftedPos * projection; + float w = shiftedPos.w; + shiftedPos.xyw = (pivotTransform * vec4(shiftedPos.xy, 0.0, w)).xyw; + shiftedPos.z *= shiftedPos.w / w; + gl_Position = shiftedPos; v_colorTexCoords = a_colorTexCoords; } diff --git a/drape/shaders/path_symbol_vertex_shader.vsh b/drape/shaders/path_symbol_vertex_shader.vsh index d7867c1029..38c6b7791a 100644 --- a/drape/shaders/path_symbol_vertex_shader.vsh +++ b/drape/shaders/path_symbol_vertex_shader.vsh @@ -1,17 +1,22 @@ -attribute vec3 a_position; +attribute vec4 a_position; attribute vec2 a_normal; attribute vec2 a_colorTexCoords; uniform mat4 modelView; uniform mat4 projection; +uniform mat4 pivotTransform; varying vec2 v_colorTexCoords; void main(void) { - lowp vec4 pos = vec4(a_position, 1) * modelView; + lowp vec4 pos = vec4(a_position.xyz, 1) * modelView; highp vec4 norm = vec4(a_normal, 0, 0) * modelView; highp vec4 shiftedPos = norm + pos; - gl_Position = shiftedPos * projection; + shiftedPos = shiftedPos * projection; + float w = shiftedPos.w; + shiftedPos.xyw = (pivotTransform * vec4(shiftedPos.xy, 0.0, w)).xyw; + shiftedPos.z *= shiftedPos.w / w; + gl_Position = shiftedPos; v_colorTexCoords = a_colorTexCoords; } diff --git a/drape/shaders/position_accuracy3d_shader.vsh b/drape/shaders/position_accuracy3d_shader.vsh new file mode 100644 index 0000000000..f2be69f8a3 --- /dev/null +++ b/drape/shaders/position_accuracy3d_shader.vsh @@ -0,0 +1,26 @@ +attribute vec2 a_normal; +attribute vec2 a_colorTexCoords; + +uniform vec3 u_position; +uniform float u_accuracy; + +uniform mat4 modelView; +uniform mat4 projection; +uniform mat4 pivotTransform; +uniform float zScale; + +varying vec2 v_colorTexCoords; + +void main(void) +{ + vec4 position = vec4(u_position.xy, 0.0, 1.0) * modelView; + vec4 normal = vec4(normalize(a_normal) * u_accuracy, 0.0, 0.0); + position = (position + normal) * projection; + + float w = position.w; + position.xyw = (pivotTransform * vec4(position.xy, u_position.z * zScale, w)).xyw; + position.z *= position.w / w; + gl_Position = position; + + v_colorTexCoords = a_colorTexCoords; +} diff --git a/drape/shaders/position_accuracy_shader.vsh b/drape/shaders/position_accuracy_shader.vsh deleted file mode 100644 index af33e3f691..0000000000 --- a/drape/shaders/position_accuracy_shader.vsh +++ /dev/null @@ -1,19 +0,0 @@ -attribute vec2 a_normal; -attribute vec2 a_colorTexCoords; - -uniform vec3 u_position; -uniform float u_accuracy; - -uniform mat4 modelView; -uniform mat4 projection; - -varying vec2 v_colorTexCoords; - -void main(void) -{ - vec4 position = vec4(u_position, 1.0) * modelView; - vec4 normal = vec4(normalize(a_normal) * u_accuracy, 0.0, 0.0); - gl_Position = (position + normal) * projection; - - v_colorTexCoords = a_colorTexCoords; -} diff --git a/drape/shaders/route_vertex_shader.vsh b/drape/shaders/route_vertex_shader.vsh index 0097ab1974..0c8cd4f0e7 100644 --- a/drape/shaders/route_vertex_shader.vsh +++ b/drape/shaders/route_vertex_shader.vsh @@ -4,6 +4,7 @@ attribute vec3 a_length; uniform mat4 modelView; uniform mat4 projection; +uniform mat4 pivotTransform; uniform vec3 u_routeParams; @@ -28,5 +29,9 @@ void main(void) } v_length = vec3(len, u_routeParams.z); - gl_Position = vec4(transformedAxisPos, a_position.z, 1.0) * projection; + vec4 pos = vec4(transformedAxisPos, a_position.z, 1.0) * projection; + float w = pos.w; + pos.xyw = (pivotTransform * vec4(pos.xy, 0.0, w)).xyw; + pos.z *= pos.w / w; + gl_Position = pos; } diff --git a/drape/shaders/shader_index.txt b/drape/shaders/shader_index.txt index edeff4488f..9186a52ea3 100644 --- a/drape/shaders/shader_index.txt +++ b/drape/shaders/shader_index.txt @@ -1,6 +1,8 @@ TEXT_OUTLINED_PROGRAM text_outlined_vertex_shader.vsh text_fragment_shader.fsh TEXT_PROGRAM text_vertex_shader.vsh text_fragment_shader.fsh +TEXT_OUTLINED_GUI_PROGRAM text_outlined_gui_vertex_shader.vsh text_fragment_shader.fsh AREA_PROGRAM area_vertex_shader.vsh solid_color_fragment_shader.fsh +AREA_3D_PROGRAM area3d_vertex_shader.vsh texturing3d_fragment_shader.fsh TEXTURING_PROGRAM texturing_vertex_shader.vsh texturing_fragment_shader.fsh LINE_PROGRAM line_vertex_shader.vsh line_fragment_shader.fsh CAP_JOIN_PROGRAM circle_shader.vsh circle_shader.fsh @@ -8,10 +10,16 @@ DASHED_LINE_PROGRAM dashed_vertex_shader.vsh dashed_fragment_shader.fsh PATH_SYMBOL_LINE path_symbol_vertex_shader.vsh texturing_fragment_shader.fsh COMPASS_PROGRAM compass_vertex_shader.vsh texturing_fragment_shader.fsh RULER_PROGRAM ruler_vertex_shader.vsh texturing_fragment_shader.fsh -ACCURACY_PROGRAM position_accuracy_shader.vsh texturing_fragment_shader.fsh +ACCURACY_PROGRAM position_accuracy3d_shader.vsh texturing_fragment_shader.fsh MY_POSITION_PROGRAM my_position_shader.vsh texturing_fragment_shader.fsh BUTTON_PROGRAM button_vertex_shader.vsh button_fragment_shader.fsh BOOKMARK_PROGRAM user_mark.vsh texturing_fragment_shader.fsh ROUTE_PROGRAM route_vertex_shader.vsh route_fragment_shader.fsh ROUTE_ARROW_PROGRAM route_vertex_shader.vsh route_arrow_fragment_shader.fsh DEBUG_RECT_PROGRAM debug_rect_vertex_shader.vsh debug_rect_fragment_shader.fsh +TRANSPARENT_LAYER_PROGRAM transparent_layer_vertex_shader.vsh transparent_layer_fragment_shader.fsh +ARROW_3D_PROGRAM arrow3d_vertex_shader.vsh arrow3d_fragment_shader.fsh +TEXTURING_BILLBOARD_PROGRAM texturing_billboard_vertex_shader.vsh texturing_fragment_shader.fsh +TEXT_OUTLINED_BILLBOARD_PROGRAM text_outlined_billboard_vertex_shader.vsh text_fragment_shader.fsh +TEXT_BILLBOARD_PROGRAM text_billboard_vertex_shader.vsh text_fragment_shader.fsh +BOOKMARK_BILLBOARD_PROGRAM user_mark_billboard.vsh texturing_fragment_shader.fsh diff --git a/drape/shaders/text_billboard_vertex_shader.vsh b/drape/shaders/text_billboard_vertex_shader.vsh new file mode 100755 index 0000000000..be61ec8353 --- /dev/null +++ b/drape/shaders/text_billboard_vertex_shader.vsh @@ -0,0 +1,43 @@ +attribute vec4 a_position; +attribute vec2 a_normal; +attribute vec2 a_colorTexCoord; +attribute vec2 a_maskTexCoord; + +uniform mat4 modelView; +uniform mat4 projection; +uniform mat4 pivotTransform; +uniform float u_isOutlinePass; +uniform float zScale; + +#ifdef ENABLE_VTF +uniform sampler2D u_colorTex; +varying lowp vec4 v_color; +#else +varying vec2 v_colorTexCoord; +#endif + +varying vec2 v_maskTexCoord; + +void main() +{ + // Here we intentionally decrease precision of 'pivot' calculation + // to eliminate jittering effect in process of billboard reconstruction. + lowp vec4 pivot = vec4(a_position.xyz, 1.0) * modelView; + vec4 offset = vec4(a_normal, 0.0, 0.0) * projection; + + float pivotZ = a_position.w; + + vec4 projectedPivot = pivot * projection; + float logicZ = projectedPivot.z / projectedPivot.w; + vec4 transformedPivot = pivotTransform * vec4(projectedPivot.xy, pivotZ * zScale, projectedPivot.w); + + vec4 scale = pivotTransform * vec4(1.0, -1.0, 0.0, 1.0); + gl_Position = vec4(transformedPivot.xy / transformedPivot.w, logicZ, 1.0) + vec4(offset.xy / scale.w * scale.x, 0.0, 0.0); + +#ifdef ENABLE_VTF + v_color = texture2D(u_colorTex, a_colorTexCoord); +#else + v_colorTexCoord = a_colorTexCoord; +#endif + v_maskTexCoord = a_maskTexCoord; +} diff --git a/drape/shaders/text_outlined_billboard_vertex_shader.vsh b/drape/shaders/text_outlined_billboard_vertex_shader.vsh new file mode 100755 index 0000000000..706de84d4d --- /dev/null +++ b/drape/shaders/text_outlined_billboard_vertex_shader.vsh @@ -0,0 +1,51 @@ +attribute vec4 a_position; +attribute vec2 a_normal; +attribute vec2 a_colorTexCoord; +attribute vec2 a_outlineColorTexCoord; +attribute vec2 a_maskTexCoord; + +uniform mat4 modelView; +uniform mat4 projection; +uniform mat4 pivotTransform; +uniform float u_isOutlinePass; +uniform float zScale; + +#ifdef ENABLE_VTF +uniform sampler2D u_colorTex; +varying lowp vec4 v_color; +#else +varying vec2 v_colorTexCoord; +#endif + +varying vec2 v_maskTexCoord; + +const float BaseDepthShift = -10.0; + +void main() +{ + float isOutline = step(0.5, u_isOutlinePass); + float notOutline = 1.0 - isOutline; + float depthShift = BaseDepthShift * isOutline; + + // Here we intentionally decrease precision of 'pivot' calculation + // to eliminate jittering effect in process of billboard reconstruction. + lowp vec4 pivot = (vec4(a_position.xyz, 1.0) + vec4(0.0, 0.0, depthShift, 0.0)) * modelView; + vec4 offset = vec4(a_normal, 0.0, 0.0) * projection; + + float pivotZ = a_position.w; + + vec4 projectedPivot = pivot * projection; + float logicZ = projectedPivot.z / projectedPivot.w; + vec4 transformedPivot = pivotTransform * vec4(projectedPivot.xy, pivotZ * zScale, projectedPivot.w); + + vec4 scale = pivotTransform * vec4(1.0, -1.0, 0.0, 1.0); + gl_Position = vec4(transformedPivot.xy / transformedPivot.w, logicZ, 1.0) + vec4(offset.xy / scale.w * scale.x, 0.0, 0.0); + + vec2 colorTexCoord = a_colorTexCoord * notOutline + a_outlineColorTexCoord * isOutline; +#ifdef ENABLE_VTF + v_color = texture2D(u_colorTex, colorTexCoord); +#else + v_colorTexCoord = colorTexCoord; +#endif + v_maskTexCoord = a_maskTexCoord; +} diff --git a/drape/shaders/text_outlined_gui_vertex_shader.vsh b/drape/shaders/text_outlined_gui_vertex_shader.vsh new file mode 100755 index 0000000000..6a60995631 --- /dev/null +++ b/drape/shaders/text_outlined_gui_vertex_shader.vsh @@ -0,0 +1,42 @@ +attribute vec4 a_position; +attribute vec2 a_normal; +attribute vec2 a_colorTexCoord; +attribute vec2 a_outlineColorTexCoord; +attribute vec2 a_maskTexCoord; + +uniform mat4 modelView; +uniform mat4 projection; +uniform float u_isOutlinePass; + +#ifdef ENABLE_VTF +uniform sampler2D u_colorTex; +varying lowp vec4 v_color; +#else +varying vec2 v_colorTexCoord; +#endif + +varying vec2 v_maskTexCoord; + +const float Zero = 0.0; +const float One = 1.0; +const float BaseDepthShift = -10.0; + +void main() +{ + float isOutline = step(0.5, u_isOutlinePass); + float notOutline = One - isOutline; + float depthShift = BaseDepthShift * isOutline; + + // Here we intentionally decrease precision of 'pos' calculation + // to eliminate jittering effect in process of billboard reconstruction. + lowp vec4 pos = (vec4(a_position.xyz, 1.0) + vec4(Zero, Zero, depthShift, Zero)) * modelView; + highp vec4 shiftedPos = vec4(a_normal, Zero, Zero) + pos; + gl_Position = shiftedPos * projection; + vec2 colorTexCoord = a_colorTexCoord * notOutline + a_outlineColorTexCoord * isOutline; +#ifdef ENABLE_VTF + v_color = texture2D(u_colorTex, colorTexCoord); +#else + v_colorTexCoord = colorTexCoord; +#endif + v_maskTexCoord = a_maskTexCoord; +} diff --git a/drape/shaders/text_outlined_vertex_shader.vsh b/drape/shaders/text_outlined_vertex_shader.vsh index 6f0c4b96b3..977a3de444 100755 --- a/drape/shaders/text_outlined_vertex_shader.vsh +++ b/drape/shaders/text_outlined_vertex_shader.vsh @@ -6,6 +6,7 @@ attribute vec2 a_maskTexCoord; uniform mat4 modelView; uniform mat4 projection; +uniform mat4 pivotTransform; uniform float u_isOutlinePass; #ifdef ENABLE_VTF @@ -29,9 +30,13 @@ void main() // Here we intentionally decrease precision of 'pos' calculation // to eliminate jittering effect in process of billboard reconstruction. - lowp vec4 pos = (a_position + vec4(Zero, Zero, depthShift, Zero)) * modelView; + lowp vec4 pos = (vec4(a_position.xyz, 1) + vec4(Zero, Zero, depthShift, Zero)) * modelView; highp vec4 shiftedPos = vec4(a_normal, Zero, Zero) + pos; - gl_Position = shiftedPos * projection; + shiftedPos = shiftedPos * projection; + float w = shiftedPos.w; + shiftedPos.xyw = (pivotTransform * vec4(shiftedPos.xy, 0.0, w)).xyw; + shiftedPos.z *= shiftedPos.w / w; + gl_Position = shiftedPos; vec2 colorTexCoord = a_colorTexCoord * notOutline + a_outlineColorTexCoord * isOutline; #ifdef ENABLE_VTF v_color = texture2D(u_colorTex, colorTexCoord); diff --git a/drape/shaders/text_vertex_shader.vsh b/drape/shaders/text_vertex_shader.vsh index c455044164..e7be2fd7eb 100644 --- a/drape/shaders/text_vertex_shader.vsh +++ b/drape/shaders/text_vertex_shader.vsh @@ -5,6 +5,7 @@ attribute vec2 a_maskTexCoord; uniform mat4 modelView; uniform mat4 projection; +uniform mat4 pivotTransform; uniform float u_isOutlinePass; #ifdef ENABLE_VTF @@ -23,9 +24,14 @@ void main() { // Here we intentionally decrease precision of 'pos' calculation // to eliminate jittering effect in process of billboard reconstruction. - lowp vec4 pos = a_position * modelView; + lowp vec4 pos = vec4(a_position.xyz, 1) * modelView; highp vec4 shiftedPos = vec4(a_normal, Zero, Zero) + pos; - gl_Position = shiftedPos * projection; + shiftedPos = shiftedPos * projection; + float w = shiftedPos.w; + shiftedPos.xyw = (pivotTransform * vec4(shiftedPos.xy, 0.0, w)).xyw; + shiftedPos.z *= shiftedPos.w / w; + gl_Position = shiftedPos; + #ifdef ENABLE_VTF v_color = texture2D(u_colorTex, a_colorTexCoord); #else diff --git a/drape/shaders/texturing3d_fragment_shader.fsh b/drape/shaders/texturing3d_fragment_shader.fsh new file mode 100644 index 0000000000..a756729897 --- /dev/null +++ b/drape/shaders/texturing3d_fragment_shader.fsh @@ -0,0 +1,11 @@ +uniform sampler2D u_colorTex; +uniform float u_opacity; + +varying vec2 v_colorTexCoords; +varying float v_intensity; + +void main(void) +{ + vec4 finalColor = vec4(texture2D(u_colorTex, v_colorTexCoords).rgb, u_opacity); + gl_FragColor = vec4((v_intensity * 0.2 + 0.8) * finalColor.rgb, finalColor.a); +} diff --git a/drape/shaders/texturing_billboard_vertex_shader.vsh b/drape/shaders/texturing_billboard_vertex_shader.vsh new file mode 100644 index 0000000000..92e8571ec4 --- /dev/null +++ b/drape/shaders/texturing_billboard_vertex_shader.vsh @@ -0,0 +1,29 @@ +attribute vec4 a_position; +attribute vec2 a_normal; +attribute vec2 a_colorTexCoords; + +uniform mat4 modelView; +uniform mat4 projection; +uniform mat4 pivotTransform; +uniform float zScale; + +varying vec2 v_colorTexCoords; + +void main(void) +{ + // Here we intentionally decrease precision of 'pivot' calculation + // to eliminate jittering effect in process of billboard reconstruction. + lowp vec4 pivot = vec4(a_position.xyz, 1.0) * modelView; + vec4 offset = vec4(a_normal, 0.0, 0.0) * projection; + + float pivotZ = a_position.w; + + vec4 projectedPivot = pivot * projection; + float logicZ = projectedPivot.z / projectedPivot.w; + vec4 transformedPivot = pivotTransform * vec4(projectedPivot.xy, pivotZ * zScale, projectedPivot.w); + + vec4 scale = pivotTransform * vec4(1.0, -1.0, 0.0, 1.0); + gl_Position = vec4(transformedPivot.xy / transformedPivot.w, logicZ, 1.0) + vec4(offset.xy / scale.w * scale.x, 0.0, 0.0); + + v_colorTexCoords = a_colorTexCoords; +} diff --git a/drape/shaders/texturing_vertex_shader.vsh b/drape/shaders/texturing_vertex_shader.vsh index 14486125e1..d633a12540 100644 --- a/drape/shaders/texturing_vertex_shader.vsh +++ b/drape/shaders/texturing_vertex_shader.vsh @@ -1,9 +1,10 @@ -attribute vec3 a_position; +attribute vec4 a_position; attribute vec2 a_normal; attribute vec2 a_colorTexCoords; uniform mat4 modelView; uniform mat4 projection; +uniform mat4 pivotTransform; varying vec2 v_colorTexCoords; @@ -11,8 +12,12 @@ void main(void) { // Here we intentionally decrease precision of 'pos' calculation // to eliminate jittering effect in process of billboard reconstruction. - lowp vec4 pos = vec4(a_position, 1) * modelView; + lowp vec4 pos = vec4(a_position.xyz, 1) * modelView; highp vec4 shiftedPos = vec4(a_normal, 0, 0) + pos; - gl_Position = shiftedPos * projection; + shiftedPos = shiftedPos * projection; + float w = shiftedPos.w; + shiftedPos.xyw = (pivotTransform * vec4(shiftedPos.xy, 0.0, w)).xyw; + shiftedPos.z *= shiftedPos.w / w; + gl_Position = shiftedPos; v_colorTexCoords = a_colorTexCoords; } diff --git a/drape/shaders/transparent_layer_fragment_shader.fsh b/drape/shaders/transparent_layer_fragment_shader.fsh new file mode 100644 index 0000000000..79e04c6bed --- /dev/null +++ b/drape/shaders/transparent_layer_fragment_shader.fsh @@ -0,0 +1,10 @@ +uniform sampler2D tex; +varying vec2 v_tcoord; + +const float opacity = 0.7; + +void main() +{ + gl_FragColor = texture2D(tex, v_tcoord); + gl_FragColor.a = gl_FragColor.a * opacity; +} diff --git a/drape/shaders/transparent_layer_vertex_shader.vsh b/drape/shaders/transparent_layer_vertex_shader.vsh new file mode 100644 index 0000000000..a0c8f821d2 --- /dev/null +++ b/drape/shaders/transparent_layer_vertex_shader.vsh @@ -0,0 +1,11 @@ +attribute vec2 a_pos; +attribute vec2 a_tcoord; + +varying vec2 v_tcoord; + +void main() +{ + v_tcoord = a_tcoord; + gl_Position = vec4(a_pos, 0.0, 1.0); +} + diff --git a/drape/shaders/user_mark.vsh b/drape/shaders/user_mark.vsh index 1d9a77677e..724fef0756 100644 --- a/drape/shaders/user_mark.vsh +++ b/drape/shaders/user_mark.vsh @@ -5,6 +5,7 @@ attribute float a_animate; uniform mat4 modelView; uniform mat4 projection; +uniform mat4 pivotTransform; uniform float u_interpolationT; varying vec2 v_colorTexCoords; @@ -14,6 +15,11 @@ void main(void) vec2 normal = a_normal; if (a_animate > 0.0) normal = u_interpolationT * normal; - gl_Position = (vec4(normal, 0, 0) + vec4(a_position, 1) * modelView) * projection; + + vec4 pos = (vec4(normal, 0, 0) + vec4(a_position, 1) * modelView) * projection; + float w = pos.w; + pos.xyw = (pivotTransform * vec4(pos.xy, 0.0, w)).xyw; + pos.z *= pos.w / w; + gl_Position = pos; v_colorTexCoords = a_colorTexCoords; } diff --git a/drape/shaders/user_mark_billboard.vsh b/drape/shaders/user_mark_billboard.vsh new file mode 100644 index 0000000000..4ad8aa043d --- /dev/null +++ b/drape/shaders/user_mark_billboard.vsh @@ -0,0 +1,29 @@ +attribute vec3 a_position; +attribute vec2 a_normal; +attribute vec2 a_colorTexCoords; +attribute float a_animate; + +uniform mat4 modelView; +uniform mat4 projection; +uniform mat4 pivotTransform; +uniform float u_interpolationT; + +varying vec2 v_colorTexCoords; + +void main(void) +{ + vec2 normal = a_normal; + if (a_animate > 0.0) + normal = u_interpolationT * normal; + + vec4 pivot = vec4(a_position.xyz, 1) * modelView; + vec4 offset = vec4(normal, 0, 0) * projection; + + vec4 projectedPivot = pivot * projection; + vec4 transformedPivot = pivotTransform * vec4(projectedPivot.xy, 0.0, 1.0); + + vec4 scale = pivotTransform * vec4(1.0, -1.0, 0, 1.0); + gl_Position = transformedPivot + vec4(offset.xy * transformedPivot.w / scale.w * scale.x, 0, 0); + + v_colorTexCoords = a_colorTexCoords; +} diff --git a/drape/utils/vertex_decl.cpp b/drape/utils/vertex_decl.cpp index f0501bf986..f2afc6d793 100644 --- a/drape/utils/vertex_decl.cpp +++ b/drape/utils/vertex_decl.cpp @@ -9,6 +9,7 @@ namespace enum VertexType { Area, + Area3d, SolidTexturing, TextStatic, TextOutlinedStatic, @@ -33,20 +34,34 @@ dp::BindingInfo AreaBindingInit() sizeof(AreaVertex::TTexCoord)), ""); dp::BindingFiller filler(2); - filler.FillDecl("a_position"); - filler.FillDecl("a_colorTexCoords"); + filler.FillDecl("a_position"); + filler.FillDecl("a_colorTexCoords"); + + return filler.m_info; +} + +dp::BindingInfo Area3dBindingInit() +{ + static_assert(sizeof(Area3dVertex) == (sizeof(Area3dVertex::TPosition) + + sizeof(Area3dVertex::TNormal3d) + + sizeof(Area3dVertex::TTexCoord)), ""); + + dp::BindingFiller filler(3); + filler.FillDecl("a_position"); + filler.FillDecl("a_normal"); + filler.FillDecl("a_colorTexCoords"); return filler.m_info; } dp::BindingInfo SolidTexturingBindingInit() { - static_assert(sizeof(SolidTexturingVertex) == (sizeof(SolidTexturingVertex::TPosition) + + static_assert(sizeof(SolidTexturingVertex) == (sizeof(SolidTexturingVertex::TPosition3d) + sizeof(SolidTexturingVertex::TNormal) + sizeof(SolidTexturingVertex::TTexCoord)), ""); dp::BindingFiller filler(3); - filler.FillDecl("a_position"); + filler.FillDecl("a_position"); filler.FillDecl("a_normal"); filler.FillDecl("a_colorTexCoords"); @@ -55,11 +70,9 @@ dp::BindingInfo SolidTexturingBindingInit() dp::BindingInfo TextStaticBindingInit() { - static_assert(sizeof(TextStaticVertex) == (sizeof(TextOutlinedStaticVertex::TPosition) + - 2 * sizeof(TextOutlinedStaticVertex::TTexCoord)), ""); + static_assert(sizeof(TextStaticVertex) == (2 * sizeof(TextStaticVertex::TTexCoord)), ""); - dp::BindingFiller filler(3); - filler.FillDecl("a_position"); + dp::BindingFiller filler(2); filler.FillDecl("a_colorTexCoord"); filler.FillDecl("a_maskTexCoord"); @@ -68,11 +81,9 @@ dp::BindingInfo TextStaticBindingInit() dp::BindingInfo TextOutlinedStaticBindingInit() { - static_assert(sizeof(TextOutlinedStaticVertex) == (sizeof(TextOutlinedStaticVertex::TPosition) + - 3 * sizeof(TextOutlinedStaticVertex::TTexCoord)), ""); + static_assert(sizeof(TextOutlinedStaticVertex) == (3 * sizeof(TextOutlinedStaticVertex::TTexCoord)), ""); - dp::BindingFiller filler(4); - filler.FillDecl("a_position"); + dp::BindingFiller filler(3); filler.FillDecl("a_colorTexCoord"); filler.FillDecl("a_outlineColorTexCoord"); filler.FillDecl("a_maskTexCoord"); @@ -82,9 +93,11 @@ dp::BindingInfo TextOutlinedStaticBindingInit() dp::BindingInfo TextDynamicBindingInit() { - static_assert(sizeof(TextDynamicVertex) == sizeof(TextDynamicVertex::TNormal), ""); + static_assert(sizeof(TextDynamicVertex) == (sizeof(TextStaticVertex::TPosition3d) + + sizeof(TextDynamicVertex::TNormal)), ""); - dp::BindingFiller filler(1, TextDynamicVertex::GetDynamicStreamID()); + dp::BindingFiller filler(2, TextDynamicVertex::GetDynamicStreamID()); + filler.FillDecl("a_position"); filler.FillDecl("a_normal"); return filler.m_info; @@ -137,6 +150,7 @@ BindingNode g_bindingNodes[TypeCount]; TInitFunction g_initFunctions[TypeCount] = { &AreaBindingInit, + &Area3dBindingInit, &SolidTexturingBindingInit, &TextStaticBindingInit, &TextOutlinedStaticBindingInit, @@ -177,14 +191,34 @@ dp::BindingInfo const & AreaVertex::GetBindingInfo() return GetBinding(Area); } -SolidTexturingVertex::SolidTexturingVertex() +Area3dVertex::Area3dVertex() : m_position(0.0, 0.0, 0.0) + , m_normal(0.0, 0.0, 0.0) + , m_colorTexCoord(0.0, 0.0) +{ +} + +Area3dVertex::Area3dVertex(TPosition const & position, TPosition const & normal, + TTexCoord const & colorTexCoord) + : m_position(position) + , m_normal(normal) + , m_colorTexCoord(colorTexCoord) +{ +} + +dp::BindingInfo const & Area3dVertex::GetBindingInfo() +{ + return GetBinding(Area3d); +} + +SolidTexturingVertex::SolidTexturingVertex() + : m_position(0.0, 0.0, 0.0, 0.0) , m_normal(0.0, 0.0) , m_colorTexCoord(0.0, 0.0) { } -SolidTexturingVertex::SolidTexturingVertex(TPosition const & position, TNormal const & normal, +SolidTexturingVertex::SolidTexturingVertex(const TPosition3d & position, TNormal const & normal, TTexCoord const & colorTexCoord) : m_position(position) , m_normal(normal) @@ -198,17 +232,16 @@ dp::BindingInfo const & SolidTexturingVertex::GetBindingInfo() } TextOutlinedStaticVertex::TextOutlinedStaticVertex() - : m_position(0.0, 0.0, 0.0) - , m_colorTexCoord(0.0, 0.0) + : m_colorTexCoord(0.0, 0.0) , m_outlineTexCoord(0.0, 0.0) , m_maskTexCoord(0.0, 0.0) { } -TextOutlinedStaticVertex::TextOutlinedStaticVertex(TPosition const & position, TTexCoord const & colorTexCoord, - TTexCoord const & outlineTexCoord, TTexCoord const & maskTexCoord) - : m_position(position) - , m_colorTexCoord(colorTexCoord) +TextOutlinedStaticVertex::TextOutlinedStaticVertex(TTexCoord const & colorTexCoord, + TTexCoord const & outlineTexCoord, + TTexCoord const & maskTexCoord) + : m_colorTexCoord(colorTexCoord) , m_outlineTexCoord(outlineTexCoord) , m_maskTexCoord(maskTexCoord) { @@ -220,12 +253,14 @@ dp::BindingInfo const & TextOutlinedStaticVertex::GetBindingInfo() } TextDynamicVertex::TextDynamicVertex() - : m_normal(0.0, 0.0) + : m_position(0.0, 0.0, 0.0, 0.0) + , m_normal(0.0, 0.0) { } -TextDynamicVertex::TextDynamicVertex(TNormal const & normal) - : m_normal(normal) +TextDynamicVertex::TextDynamicVertex(const TPosition3d & position, TNormal const & normal) + : m_position(position), + m_normal(normal) { } @@ -295,20 +330,15 @@ dp::BindingInfo const & RouteVertex::GetBindingInfo() } TextStaticVertex::TextStaticVertex() - : m_position(0.0, 0.0, 0.0) - , m_colorTexCoord(0.0, 0.0) + : m_colorTexCoord(0.0, 0.0) , m_maskTexCoord(0.0, 0.0) { - } -TextStaticVertex::TextStaticVertex(TPosition const & position, TTexCoord const & colorTexCoord, - TTexCoord const & maskTexCoord) - : m_position(position) - , m_colorTexCoord(colorTexCoord) +TextStaticVertex::TextStaticVertex(TTexCoord const & colorTexCoord, TTexCoord const & maskTexCoord) + : m_colorTexCoord(colorTexCoord) , m_maskTexCoord(maskTexCoord) { - } dp::BindingInfo const & TextStaticVertex::GetBindingInfo() diff --git a/drape/utils/vertex_decl.hpp b/drape/utils/vertex_decl.hpp index 3ff91e2a6e..a491b2a5b5 100644 --- a/drape/utils/vertex_decl.hpp +++ b/drape/utils/vertex_decl.hpp @@ -11,7 +11,9 @@ namespace gpu struct BaseVertex { using TPosition = glsl::vec3; + using TPosition3d = glsl::vec4; using TNormal = glsl::vec2; + using TNormal3d = glsl::vec3; using TTexCoord = glsl::vec2; }; @@ -26,12 +28,24 @@ struct AreaVertex : BaseVertex static dp::BindingInfo const & GetBindingInfo(); }; +struct Area3dVertex : BaseVertex +{ + Area3dVertex(); + Area3dVertex(TPosition const & position, const TPosition & normal, TTexCoord const & colorTexCoord); + + TPosition m_position; + TNormal3d m_normal; + TTexCoord m_colorTexCoord; + + static dp::BindingInfo const & GetBindingInfo(); +}; + struct SolidTexturingVertex : BaseVertex { SolidTexturingVertex(); - SolidTexturingVertex(TPosition const & position, TNormal const & normal, TTexCoord const & colorTexCoord); + SolidTexturingVertex(TPosition3d const & position, TNormal const & normal, TTexCoord const & colorTexCoord); - TPosition m_position; + TPosition3d m_position; TNormal m_normal; TTexCoord m_colorTexCoord; @@ -43,9 +57,8 @@ typedef buffer_vector TSolidTexVertexBuffer; struct TextStaticVertex : BaseVertex { TextStaticVertex(); - TextStaticVertex(TPosition const & position, TTexCoord const & colorTexCoord, TTexCoord const & maskTexCoord); + TextStaticVertex(TTexCoord const & colorTexCoord, TTexCoord const & maskTexCoord); - TPosition m_position; TTexCoord m_colorTexCoord; TTexCoord m_maskTexCoord; @@ -58,10 +71,9 @@ struct TextOutlinedStaticVertex : BaseVertex { public: TextOutlinedStaticVertex(); - TextOutlinedStaticVertex(TPosition const & position, TTexCoord const & colorTexCoord, - TTexCoord const & outlineTexCoord, TTexCoord const & maskTexCoord); + TextOutlinedStaticVertex(TTexCoord const & colorTexCoord, TTexCoord const & outlineTexCoord, + TTexCoord const & maskTexCoord); - TPosition m_position; TTexCoord m_colorTexCoord; TTexCoord m_outlineTexCoord; TTexCoord m_maskTexCoord; @@ -74,8 +86,9 @@ typedef buffer_vector TTextOutlinedStaticVertexBu struct TextDynamicVertex : BaseVertex { TextDynamicVertex(); - TextDynamicVertex(TNormal const & normal); + TextDynamicVertex(TPosition3d const & position, TNormal const & normal); + TPosition3d m_position; TNormal m_normal; static dp::BindingInfo const & GetBindingInfo(); diff --git a/drape/vertex_array_buffer.cpp b/drape/vertex_array_buffer.cpp index ea1d353477..b4ea02b6b9 100644 --- a/drape/vertex_array_buffer.cpp +++ b/drape/vertex_array_buffer.cpp @@ -94,7 +94,9 @@ void VertexArrayBuffer::Build(ref_ptr program) if (m_moveToGpuOnBuild && !m_isPreflushed) PreflushImpl(); - ASSERT(m_VAO == 0 && m_program == nullptr, ("No-no-no! You can't rebuild VertexArrayBuffer")); + if (m_VAO != 0 && m_program == program) + return; + m_program = program; /// if OES_vertex_array_object not supported, than buffers will be bind on each Render call if (!GLExtensionsList::Instance().IsSupported(GLExtensionsList::VertexArrayObject)) @@ -103,6 +105,8 @@ void VertexArrayBuffer::Build(ref_ptr program) if (m_staticBuffers.empty()) return; + if (m_VAO != 0) + GLFunctions::glDeleteVertexArray(m_VAO); m_VAO = GLFunctions::glGenVertexArray(); Bind(); BindStaticBuffers(); diff --git a/drape_frontend/animation/perspective_animation.cpp b/drape_frontend/animation/perspective_animation.cpp new file mode 100644 index 0000000000..7509035fa5 --- /dev/null +++ b/drape_frontend/animation/perspective_animation.cpp @@ -0,0 +1,32 @@ +#include "perspective_animation.hpp" +#include "drape_frontend/animation/interpolations.hpp" + +namespace df +{ + +// static +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 /* delay */, 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..7dc59b9fb9 --- /dev/null +++ b/drape_frontend/animation/perspective_animation.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include "drape_frontend/animation/base_interpolator.hpp" + +namespace df +{ + +class PerspectiveAnimation : public BaseInterpolator +{ + using TBase = BaseInterpolator; + +public: + PerspectiveAnimation(double duration, double startRotationAngle, double endRotationAngle); + PerspectiveAnimation(double duration, double delay, double startRotationAngle, double endRotationAngle); + + static double GetRotateDuration(double startAngle, double endAngle); + + void Advance(double elapsedSeconds) override; + double GetRotationAngle() const { return m_rotationAngle; } + +private: + double const m_startRotationAngle; + double const m_endRotationAngle; + double m_rotationAngle; +}; + +} // namespace df diff --git a/drape_frontend/apply_feature_functors.cpp b/drape_frontend/apply_feature_functors.cpp index d97fe15098..3b85a7917d 100644 --- a/drape_frontend/apply_feature_functors.cpp +++ b/drape_frontend/apply_feature_functors.cpp @@ -235,8 +235,10 @@ void BaseApplyFeature::ExtractCaptionParams(CaptionDefProto const * primaryProto } ApplyPointFeature::ApplyPointFeature(TInsertShapeFn const & insertShape, FeatureID const & id, - int minVisibleScale, uint8_t rank, CaptionDescription const & captions) + int minVisibleScale, uint8_t rank, CaptionDescription const & captions, + float posZ) : TBase(insertShape, id, minVisibleScale, rank, captions) + , m_posZ(posZ) , m_hasPoint(false) , m_symbolDepth(dp::minDepth) , m_circleDepth(dp::minDepth) @@ -282,6 +284,7 @@ void ApplyPointFeature::ProcessRule(Stylist::TRuleWrapper const & rule) ExtractCaptionParams(capRule, pRule->GetCaption(1), depth, params); params.m_minVisibleScale = m_minVisibleScale; params.m_rank = m_rank; + params.m_posZ = m_posZ; if(!params.m_primaryText.empty() || !params.m_secondaryText.empty()) m_insertShape(make_unique_dp(m_centerPoint, params, hasPOI)); } @@ -312,13 +315,16 @@ void ApplyPointFeature::Finish() params.m_symbolName = m_symbolRule->name(); float const mainScale = df::VisualParams::Instance().GetVisualScale(); params.m_extendingSize = m_symbolRule->has_min_distance() ? mainScale * m_symbolRule->min_distance() : 0; + params.m_posZ = m_posZ; m_insertShape(make_unique_dp(m_centerPoint, params)); } } -ApplyAreaFeature::ApplyAreaFeature(TInsertShapeFn const & insertShape, FeatureID const & id, +ApplyAreaFeature::ApplyAreaFeature(TInsertShapeFn const & insertShape, FeatureID const & id, float minPosZ, float posZ, int minVisibleScale, uint8_t rank, CaptionDescription const & captions) - : TBase(insertShape, id, minVisibleScale, rank, captions) + : TBase(insertShape, id, minVisibleScale, rank, captions, posZ) + , m_minPosZ(minPosZ) + , m_isBuilding(posZ > 0.0f) {} void ApplyAreaFeature::operator()(m2::PointD const & p1, m2::PointD const & p2, m2::PointD const & p3) @@ -328,11 +334,95 @@ void ApplyAreaFeature::operator()(m2::PointD const & p1, m2::PointD const & p2, { m_triangles.push_back(p2); m_triangles.push_back(p3); + if (m_isBuilding) + BuildEdges(GetIndex(p1), GetIndex(p2), GetIndex(p3)); } else { m_triangles.push_back(p3); m_triangles.push_back(p2); + if (m_isBuilding) + BuildEdges(GetIndex(p1), GetIndex(p3), GetIndex(p2)); + } +} + +int ApplyAreaFeature::GetIndex(m2::PointD const & pt) +{ + int maxIndex = -1; + for (auto it = m_indices.begin(); it != m_indices.end(); ++it) + { + if (it->first > maxIndex) + maxIndex = it->first; + + if (pt.EqualDxDy(it->second, 1e-7)) + return it->first; + } + + int const newIndex = maxIndex + 1; + m_indices.insert(make_pair(newIndex, pt)); + return newIndex; +} + +bool ApplyAreaFeature::EqualEdges(TEdge const & edge1, TEdge const & edge2) const +{ + return (edge1.first == edge2.first && edge1.second == edge2.second) || + (edge1.first == edge2.second && edge1.second == edge2.first); +} + +bool ApplyAreaFeature::FindEdge(TEdge const & edge) +{ + for (size_t i = 0; i < m_edges.size(); i++) + { + if (EqualEdges(m_edges[i].first, edge)) + { + m_edges[i].second = -1; + return true; + } + } + return false; +} + +m2::PointD ApplyAreaFeature::CalculateNormal(m2::PointD const & p1, m2::PointD const & p2, m2::PointD const & p3) const +{ + m2::PointD const tangent = (p2 - p1).Normalize(); + m2::PointD normal = m2::PointD(-tangent.y, tangent.x); + m2::PointD const v = ((p1 + p2) * 0.5 - p3).Normalize(); + if (m2::DotProduct(normal, v) < 0.0) + normal = -normal; + + return normal; +} + +void ApplyAreaFeature::BuildEdges(int vertexIndex1, int vertexIndex2, int vertexIndex3) +{ + // Check if triangle is degenerate. + if (vertexIndex1 == vertexIndex2 || vertexIndex2 == vertexIndex3 || vertexIndex1 == vertexIndex3) + return; + + TEdge edge1 = make_pair(vertexIndex1, vertexIndex2); + if (!FindEdge(edge1)) + m_edges.push_back(make_pair(move(edge1), vertexIndex3)); + + TEdge edge2 = make_pair(vertexIndex2, vertexIndex3); + if (!FindEdge(edge2)) + m_edges.push_back(make_pair(move(edge2), vertexIndex1)); + + TEdge edge3 = make_pair(vertexIndex3, vertexIndex1); + if (!FindEdge(edge3)) + m_edges.push_back(make_pair(move(edge3), vertexIndex2)); +} + +void ApplyAreaFeature::CalculateBuildingEdges(vector & edges) +{ + for (auto & e : m_edges) + { + if (e.second < 0) + continue; + BuildingEdge edge; + edge.m_startVertex = m_indices[e.first.first]; + edge.m_endVertex = m_indices[e.first.second]; + edge.m_normal = CalculateNormal(edge.m_startVertex, edge.m_endVertex, m_indices[e.second]); + edges.push_back(move(edge)); } } @@ -349,7 +439,17 @@ void ApplyAreaFeature::ProcessRule(Stylist::TRuleWrapper const & rule) params.m_color = ToDrapeColor(areaRule->color()); params.m_minVisibleScale = m_minVisibleScale; params.m_rank = m_rank; - m_insertShape(make_unique_dp(move(m_triangles), params)); + params.m_minPosZ = m_minPosZ; + params.m_posZ = m_posZ; + + vector edges; + if (m_isBuilding) + { + edges.reserve(m_edges.size()); + CalculateBuildingEdges(edges); + } + + m_insertShape(make_unique_dp(move(m_triangles), move(edges), params)); } else TBase::ProcessRule(rule); diff --git a/drape_frontend/apply_feature_functors.hpp b/drape_frontend/apply_feature_functors.hpp index 429acf4e6f..8edaa36c86 100644 --- a/drape_frontend/apply_feature_functors.hpp +++ b/drape_frontend/apply_feature_functors.hpp @@ -10,6 +10,8 @@ #include "geometry/point2d.hpp" #include "geometry/spline.hpp" +#include "std/unordered_map.hpp" + class CaptionDefProto; class CircleRuleProto; class ShieldRuleProto; @@ -22,6 +24,7 @@ namespace df struct TextViewParams; class MapShape; +struct BuildingEdge; using TInsertShapeFn = function && shape)>; @@ -51,12 +54,16 @@ class ApplyPointFeature : public BaseApplyFeature public: ApplyPointFeature(TInsertShapeFn const & insertShape, FeatureID const & id, - int minVisibleScale, uint8_t rank, CaptionDescription const & captions); + int minVisibleScale, uint8_t rank, CaptionDescription const & captions, + float posZ); void operator()(m2::PointD const & point); void ProcessRule(Stylist::TRuleWrapper const & rule); void Finish(); +protected: + float const m_posZ; + private: bool m_hasPoint; double m_symbolDepth; @@ -71,7 +78,7 @@ class ApplyAreaFeature : public ApplyPointFeature using TBase = ApplyPointFeature; public: - ApplyAreaFeature(TInsertShapeFn const & insertShape, FeatureID const & id, + ApplyAreaFeature(TInsertShapeFn const & insertShape, FeatureID const & id, float minPosZ, float posZ, int minVisibleScale, uint8_t rank, CaptionDescription const & captions); using TBase::operator (); @@ -80,7 +87,21 @@ public: void ProcessRule(Stylist::TRuleWrapper const & rule); private: + using TEdge = pair; + + void CalculateBuildingEdges(vector & edges); + int GetIndex(m2::PointD const & pt); + void BuildEdges(int vertexIndex1, int vertexIndex2, int vertexIndex3); + bool EqualEdges(TEdge const & edge1, TEdge const & edge2) const; + bool FindEdge(TEdge const & edge); + m2::PointD CalculateNormal(m2::PointD const & p1, m2::PointD const & p2, m2::PointD const & p3) const; + vector m_triangles; + + unordered_map m_indices; + vector> m_edges; + float const m_minPosZ; + bool const m_isBuilding; }; class ApplyLineFeature : public BaseApplyFeature diff --git a/drape_frontend/area_shape.cpp b/drape_frontend/area_shape.cpp index 7ae49ce7fd..59d632eda5 100644 --- a/drape_frontend/area_shape.cpp +++ b/drape_frontend/area_shape.cpp @@ -15,8 +15,10 @@ namespace df { -AreaShape::AreaShape(vector && triangleList, AreaViewParams const & params) - : m_vertexes(triangleList) +AreaShape::AreaShape(vector && triangleList, vector && buildingEdges, + AreaViewParams const & params) + : m_vertexes(move(triangleList)) + , m_buildingEdges(move(buildingEdges)) , m_params(params) { } @@ -27,20 +29,58 @@ void AreaShape::Draw(ref_ptr batcher, ref_ptr t textures->GetColorRegion(m_params.m_color, region); glsl::vec2 const colorPoint = glsl::ToVec2(region.GetTexRect().Center()); - buffer_vector vertexes; - vertexes.resize(m_vertexes.size()); - transform(m_vertexes.begin(), m_vertexes.end(), vertexes.begin(), [&colorPoint, this](m2::PointF const & vertex) + if (!m_buildingEdges.empty()) { - return gpu::AreaVertex(glsl::vec3(glsl::ToVec2(vertex), m_params.m_depth), - colorPoint); - }); + vector vertexes; + vertexes.reserve(m_vertexes.size() + m_buildingEdges.size() * 6); - dp::GLState state(gpu::AREA_PROGRAM, dp::GLState::GeometryLayer); - state.SetColorTexture(region.GetTexture()); + for (auto const & edge : m_buildingEdges) + { + glsl::vec3 normal(glsl::ToVec2(edge.m_normal), 0.0f); + vertexes.emplace_back(gpu::Area3dVertex(glsl::vec3(glsl::ToVec2(edge.m_startVertex), -m_params.m_minPosZ), + normal, colorPoint)); + vertexes.emplace_back(gpu::Area3dVertex(glsl::vec3(glsl::ToVec2(edge.m_endVertex), -m_params.m_minPosZ), + normal, colorPoint)); + vertexes.emplace_back(gpu::Area3dVertex(glsl::vec3(glsl::ToVec2(edge.m_startVertex), -m_params.m_posZ), + normal, colorPoint)); - dp::AttributeProvider provider(1, m_vertexes.size()); - provider.InitStream(0, gpu::AreaVertex::GetBindingInfo(), make_ref(vertexes.data())); - batcher->InsertTriangleList(state, make_ref(&provider)); + vertexes.emplace_back(gpu::Area3dVertex(glsl::vec3(glsl::ToVec2(edge.m_startVertex), -m_params.m_posZ), + normal, colorPoint)); + vertexes.emplace_back(gpu::Area3dVertex(glsl::vec3(glsl::ToVec2(edge.m_endVertex), -m_params.m_minPosZ), + normal, colorPoint)); + vertexes.emplace_back(gpu::Area3dVertex(glsl::vec3(glsl::ToVec2(edge.m_endVertex), -m_params.m_posZ), + normal, colorPoint)); + } + + glsl::vec3 normal(0.0f, 0.0f, -1.0f); + for (auto const & vertex : m_vertexes) + vertexes.emplace_back(gpu::Area3dVertex(glsl::vec3(glsl::ToVec2(vertex), -m_params.m_posZ), + normal, colorPoint)); + + dp::GLState state(gpu::AREA_3D_PROGRAM, dp::GLState::GeometryLayer); + state.SetColorTexture(region.GetTexture()); + state.SetBlending(dp::Blending(false /* isEnabled */)); + + dp::AttributeProvider provider(1, vertexes.size()); + provider.InitStream(0, gpu::Area3dVertex::GetBindingInfo(), make_ref(vertexes.data())); + batcher->InsertTriangleList(state, make_ref(&provider)); + } + else + { + buffer_vector vertexes; + vertexes.resize(m_vertexes.size()); + transform(m_vertexes.begin(), m_vertexes.end(), vertexes.begin(), [&colorPoint, this](m2::PointF const & vertex) + { + return gpu::AreaVertex(glsl::vec3(glsl::ToVec2(vertex), m_params.m_depth), colorPoint); + }); + + dp::GLState state(gpu::AREA_PROGRAM, dp::GLState::GeometryLayer); + state.SetColorTexture(region.GetTexture()); + + dp::AttributeProvider provider(1, m_vertexes.size()); + provider.InitStream(0, gpu::AreaVertex::GetBindingInfo(), make_ref(vertexes.data())); + batcher->InsertTriangleList(state, make_ref(&provider)); + } } } // namespace df diff --git a/drape_frontend/area_shape.hpp b/drape_frontend/area_shape.hpp index fc497a7414..5c3d30bfba 100644 --- a/drape_frontend/area_shape.hpp +++ b/drape_frontend/area_shape.hpp @@ -11,10 +11,18 @@ namespace df { +struct BuildingEdge +{ + m2::PointD m_startVertex; + m2::PointD m_endVertex; + m2::PointD m_normal; +}; + class AreaShape : public MapShape { public: - AreaShape(vector && triangleList, AreaViewParams const & params); + AreaShape(vector && triangleList, vector && buildingEdges, + AreaViewParams const & params); void Draw(ref_ptr batcher, ref_ptr textures) const override; @@ -22,6 +30,7 @@ public: private: vector m_vertexes; + vector m_buildingEdges; AreaViewParams m_params; }; diff --git a/drape_frontend/arrow3d.cpp b/drape_frontend/arrow3d.cpp new file mode 100644 index 0000000000..6b83cd4407 --- /dev/null +++ b/drape_frontend/arrow3d.cpp @@ -0,0 +1,175 @@ +#include "arrow3d.hpp" + +#include "drape/glconstants.hpp" +#include "drape/glfunctions.hpp" +#include "drape/glsl_func.hpp" +#include "drape/glsl_types.hpp" +#include "drape/glstate.hpp" +#include "drape/gpu_program_manager.hpp" +#include "drape/shader_def.hpp" +#include "drape/uniform_values_storage.hpp" + +#include "geometry/screenbase.hpp" + +namespace df +{ + +double const kArrowSizeX = 2.0; +double const kArrowSizeY = 3.0; +double const kArrow3dScale = 1.2; + +Arrow3d::Arrow3d() + : m_state(gpu::ARROW_3D_PROGRAM, dp::GLState::OverlayLayer) +{ + m_vertices = { + 0.0f, 0.0f, -1.0f, + -1.0f, -1.0f, 0.0f, + 0.0f, 2.0f, 0.0f, + + 0.0f, 0.0f, -1.0f, + 0.0f, 2.0f, 0.0f, + 1.0f, -1.0f, 0.0f, + + 0.0f, 0.0f, -1.0f, + 0.0f, -0.5f, 0.0f, + -1.0f, -1.0f, 0.0f, + + 0.0f, 0.0f, -1.0f, + 1.0f, -1.0f, 0.0f, + 0.0f, -0.5f, 0.0f + }; + + m_normals.resize(m_vertices.size()); + for (size_t triangle = 0; triangle < m_vertices.size() / 9; ++triangle) + { + glsl::vec3 v[3]; + for (size_t vertex = 0; vertex < 3; ++vertex) + { + size_t const offset = triangle * 9 + vertex * 3; + v[vertex] = glsl::vec3(m_vertices[offset], m_vertices[offset + 1], m_vertices[offset + 2]); + } + + glsl::vec3 normal = glsl::cross(glsl::vec3(v[1].x - v[0].x, v[1].y - v[0].y, v[1].z - v[0].z), + glsl::vec3(v[2].x - v[0].x, v[2].y - v[0].y, v[2].z - v[0].z)); + normal = glsl::normalize(normal); + + for (size_t vertex = 0; vertex < 3; ++vertex) + { + size_t const offset = triangle * 9 + vertex * 3; + m_normals[offset] = normal.x; + m_normals[offset + 1] = normal.y; + m_normals[offset + 2] = normal.z; + } + } +} + +Arrow3d::~Arrow3d() +{ + if (m_bufferId != 0) + GLFunctions::glDeleteBuffer(m_bufferId); + + if (m_bufferNormalsId != 0) + GLFunctions::glDeleteBuffer(m_bufferNormalsId); +} + +void Arrow3d::SetPosition(const m2::PointD & position) +{ + m_position = position; +} + +void Arrow3d::SetAzimuth(double azimuth) +{ + m_azimuth = azimuth; +} + +void Arrow3d::SetSize(uint32_t width, uint32_t height) +{ + m_pixelWidth = width; + m_pixelHeight = height; +} + +void Arrow3d::Build(ref_ptr prg) +{ + m_bufferId = GLFunctions::glGenBuffer(); + GLFunctions::glBindBuffer(m_bufferId, gl_const::GLArrayBuffer); + + m_attributePosition = prg->GetAttributeLocation("a_pos"); + ASSERT_NOT_EQUAL(m_attributePosition, -1, ()); + + GLFunctions::glBufferData(gl_const::GLArrayBuffer, m_vertices.size() * sizeof(m_vertices[0]), + m_vertices.data(), gl_const::GLStaticDraw); + + m_bufferNormalsId = GLFunctions::glGenBuffer(); + GLFunctions::glBindBuffer(m_bufferNormalsId, gl_const::GLArrayBuffer); + + m_attributeNormal = prg->GetAttributeLocation("a_normal"); + ASSERT_NOT_EQUAL(m_attributeNormal, -1, ()); + + GLFunctions::glBufferData(gl_const::GLArrayBuffer, m_normals.size() * sizeof(m_normals[0]), + m_normals.data(), gl_const::GLStaticDraw); + + GLFunctions::glBindBuffer(0, gl_const::GLArrayBuffer); +} + +void Arrow3d::Render(ScreenBase const & screen, ref_ptr mng) +{ + // Unbind current VAO, because glVertexAttributePointer and glEnableVertexAttribute can affect it. + GLFunctions::glBindVertexArray(0); + + ref_ptr prg = mng->GetProgram(gpu::ARROW_3D_PROGRAM); + prg->Bind(); + + if (!m_isInitialized) + { + Build(prg); + m_isInitialized = true; + } + + dp::ApplyState(m_state, prg); + + double const scaleX = m_pixelWidth * kArrow3dScale * 2.0 / screen.PixelRect().SizeX() / kArrowSizeX; + double const scaleY = m_pixelHeight * kArrow3dScale * 2.0 / screen.PixelRect().SizeY() / kArrowSizeY; + double const scaleZ = scaleX; + + m2::PointD const pos = screen.GtoP(m_position); + double const dX = 2.0 * pos.x / screen.PixelRect().SizeX() - 1.0; + double const dY = 2.0 * pos.y / screen.PixelRect().SizeY() - 1.0; + + math::Matrix scaleM = math::Identity(); + scaleM(0, 0) = scaleX; + scaleM(1, 1) = scaleY; + scaleM(2, 2) = scaleZ; + + math::Matrix rotateM = math::Identity(); + rotateM(0, 0) = cos(m_azimuth + screen.GetAngle()); + rotateM(0, 1) = -sin(m_azimuth + screen.GetAngle()); + rotateM(1, 0) = -rotateM(0, 1); + rotateM(1, 1) = rotateM(0, 0); + + math::Matrix translateM = math::Identity(); + translateM(3, 0) = dX; + translateM(3, 1) = -dY; + + math::Matrix modelTransform = rotateM * scaleM * translateM; + modelTransform = modelTransform * math::Matrix(screen.Pto3dMatrix()); + + dp::UniformValuesStorage uniforms; + uniforms.SetMatrix4x4Value("m_transform", modelTransform.m_data); + + dp::ApplyUniforms(uniforms, prg); + + GLFunctions::glBindBuffer(m_bufferId, gl_const::GLArrayBuffer); + GLFunctions::glEnableVertexAttribute(m_attributePosition); + GLFunctions::glVertexAttributePointer(m_attributePosition, 3, gl_const::GLFloatType, false, 0, 0); + + GLFunctions::glBindBuffer(m_bufferNormalsId, gl_const::GLArrayBuffer); + GLFunctions::glEnableVertexAttribute(m_attributeNormal); + GLFunctions::glVertexAttributePointer(m_attributeNormal, 3, gl_const::GLFloatType, false, 0, 0); + + GLFunctions::glDrawArrays(gl_const::GLTriangles, 0, m_vertices.size() / 3); + + prg->Unbind(); + GLFunctions::glBindBuffer(0, gl_const::GLArrayBuffer); +} + +} // namespace df diff --git a/drape_frontend/arrow3d.hpp b/drape_frontend/arrow3d.hpp new file mode 100644 index 0000000000..23a4622c85 --- /dev/null +++ b/drape_frontend/arrow3d.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include "drape/glstate.hpp" +#include "drape/pointers.hpp" + +#include "geometry/rect2d.hpp" + +#include "std/vector.hpp" + +namespace dp +{ +class GpuProgram; +class GpuProgramManager; +} + +class ScreenBase; + +namespace df +{ + +class Arrow3d +{ +public: + Arrow3d(); + ~Arrow3d(); + + void SetPosition(m2::PointD const & position); + void SetAzimuth(double azimuth); + void SetSize(uint32_t width, uint32_t height); + + void Render(ScreenBase const & screen, ref_ptr mng); + +private: + void Build(ref_ptr prg); + + m2::PointD m_position; + double m_azimuth; + + uint32_t m_pixelWidth = 0; + uint32_t m_pixelHeight = 0; + + uint32_t m_bufferId = 0; + uint32_t m_bufferNormalsId = 0; + + int8_t m_attributePosition; + int8_t m_attributeNormal; + + vector m_vertices; + vector m_normals; + + dp::GLState m_state; + + bool m_isInitialized = false; +}; + +} // namespace df + diff --git a/drape_frontend/backend_renderer.cpp b/drape_frontend/backend_renderer.cpp index e3656bd416..2f54a2ec66 100644 --- a/drape_frontend/backend_renderer.cpp +++ b/drape_frontend/backend_renderer.cpp @@ -97,7 +97,8 @@ void BackendRenderer::AcceptMessage(ref_ptr message) if (!tiles.empty()) { ScreenBase const screen = m_requestedTiles->GetScreen(); - m_readManager->UpdateCoverage(screen, tiles, m_texMng); + bool const is3dBuildings = m_requestedTiles->Is3dBuildings(); + m_readManager->UpdateCoverage(screen, is3dBuildings, tiles, m_texMng); gui::CountryStatusHelper & helper = gui::DrapeGui::Instance().GetCountryStatusHelper(); if ((*tiles.begin()).m_zoomLevel > scales::GetUpperWorldScale()) @@ -237,6 +238,12 @@ void BackendRenderer::AcceptMessage(ref_ptr message) ProcessStopRenderingMessage(); break; } + case Message::Allow3dBuildings: + { + ref_ptr msg = message; + m_readManager->Allow3dBuildings(msg->Allow3dBuildings()); + break; + } default: ASSERT(false, ()); break; diff --git a/drape_frontend/circle_shape.cpp b/drape_frontend/circle_shape.cpp index 24ede7440a..60878e4054 100644 --- a/drape_frontend/circle_shape.cpp +++ b/drape_frontend/circle_shape.cpp @@ -29,7 +29,7 @@ void CircleShape::Draw(ref_ptr batcher, ref_ptr buffer_vector vertexes; vertexes.push_back(gpu::SolidTexturingVertex { - glsl::vec3(glsl::ToVec2(m_pt), m_params.m_depth), + glsl::vec4(glsl::ToVec2(m_pt), m_params.m_depth, 0.0f), glsl::vec2(0.0f, 0.0f), colorPoint }); @@ -41,7 +41,7 @@ void CircleShape::Draw(ref_ptr batcher, ref_ptr m2::PointD rotatedNormal = m2::Rotate(startNormal, (i) * etalonSector); vertexes.push_back(gpu::SolidTexturingVertex { - glsl::vec3(glsl::ToVec2(m_pt), m_params.m_depth), + glsl::vec4(glsl::ToVec2(m_pt), m_params.m_depth, 0.0f), glsl::ToVec2(rotatedNormal), colorPoint }); diff --git a/drape_frontend/drape_engine.cpp b/drape_frontend/drape_engine.cpp index 3cc873cac0..6b753d01ba 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,4 +429,23 @@ gui::TWidgetsSizeInfo const & DrapeEngine::GetWidgetSizes() return m_widgetSizes; } +void DrapeEngine::Allow3dMode(bool allowPerspectiveInNavigation, bool allow3dBuildings, double rotationAngle, double angleFOV) +{ + m_threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread, + make_unique_dp(allow3dBuildings), + MessagePriority::Normal); + + m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread, + make_unique_dp(allowPerspectiveInNavigation, allow3dBuildings, + rotationAngle, angleFOV), + MessagePriority::Normal); +} + +void DrapeEngine::EnablePerspective(double rotationAngle, double angleFOV) +{ + m_threadCommutator->PostMessage(ThreadsCommutator::RenderThread, + make_unique_dp(rotationAngle, angleFOV), + MessagePriority::Normal); +} + } // namespace df diff --git a/drape_frontend/drape_engine.hpp b/drape_frontend/drape_engine.hpp index c7b10e4e5b..8184a79177 100644 --- a/drape_frontend/drape_engine.hpp +++ b/drape_frontend/drape_engine.hpp @@ -107,13 +107,16 @@ 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 Allow3dMode(bool allowPerspectiveInNavigation, bool allow3dBuildings, double rotationAngle, double angleFOV); + void EnablePerspective(double rotationAngle, double angleFOV); + private: void AddUserEvent(UserEvent const & e); void ModelViewChanged(ScreenBase const & screen); diff --git a/drape_frontend/drape_frontend.pro b/drape_frontend/drape_frontend.pro index e67ab21921..e947587be6 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 \ @@ -32,12 +33,14 @@ SOURCES += \ gui/skin.cpp \ apply_feature_functors.cpp \ area_shape.cpp \ + arrow3d.cpp \ backend_renderer.cpp \ base_renderer.cpp \ batchers_pool.cpp \ circle_shape.cpp \ drape_engine.cpp \ engine_context.cpp \ + framebuffer.cpp \ frontend_renderer.cpp \ line_shape.cpp \ line_shape_helper.cpp \ @@ -71,6 +74,7 @@ SOURCES += \ tile_tree.cpp \ tile_tree_builder.cpp \ tile_utils.cpp \ + transparent_layer.cpp \ user_event_stream.cpp \ user_mark_shapes.cpp \ user_marks_provider.cpp \ @@ -94,6 +98,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 \ @@ -110,12 +115,14 @@ HEADERS += \ gui/skin.hpp \ apply_feature_functors.hpp \ area_shape.hpp \ + arrow3d.hpp \ backend_renderer.hpp \ base_renderer.hpp \ batchers_pool.hpp \ circle_shape.hpp \ drape_engine.hpp \ engine_context.hpp \ + framebuffer.hpp \ frontend_renderer.hpp \ intrusive_vector.hpp \ line_shape.hpp \ @@ -154,6 +161,7 @@ HEADERS += \ tile_tree.hpp \ tile_tree_builder.hpp \ tile_utils.hpp \ + transparent_layer.hpp \ user_event_stream.hpp \ user_mark_shapes.hpp \ user_marks_provider.hpp \ diff --git a/drape_frontend/framebuffer.cpp b/drape_frontend/framebuffer.cpp new file mode 100644 index 0000000000..21e0d85dc9 --- /dev/null +++ b/drape_frontend/framebuffer.cpp @@ -0,0 +1,106 @@ +#include "framebuffer.hpp" + +#include "drape/glfunctions.hpp" +#include "drape/oglcontext.hpp" + +#include "base/assert.hpp" +#include "base/logging.hpp" +#include "base/string_utils.hpp" + +#include "math.h" + +namespace df +{ + +Framebuffer::~Framebuffer() +{ + Destroy(); +} + +void Framebuffer::Destroy() +{ + if (m_colorTextureId != 0) + { + GLFunctions::glDeleteTexture(m_colorTextureId); + m_colorTextureId = 0; + } + if (m_depthTextureId != 0) + { + GLFunctions::glDeleteTexture(m_depthTextureId); + m_depthTextureId = 0; + } + if (m_framebufferId != 0) + { + GLFunctions::glDeleteFramebuffer(&m_framebufferId); + m_framebufferId = 0; + } +} + +void Framebuffer::SetDefaultContext(dp::OGLContext * context) +{ + m_defaultContext = context; +} + +int32_t Framebuffer::GetMaxSize() +{ + if (m_maxTextureSize == 0) + m_maxTextureSize = GLFunctions::glGetInteger(gl_const::GLMaxTextureSize); + return m_maxTextureSize; +} + +void Framebuffer::SetSize(uint32_t width, uint32_t height) +{ + ASSERT(m_defaultContext, ()); + + if (m_width == width && m_height == height) + return; + + m_height = height; + m_width = width; + + Destroy(); + + m_colorTextureId = GLFunctions::glGenTexture(); + GLFunctions::glBindTexture(m_colorTextureId); + GLFunctions::glTexImage2D(m_width, m_height, gl_const::GLRGBA, gl_const::GLUnsignedByteType, NULL); + GLFunctions::glTexParameter(gl_const::GLMagFilter, gl_const::GLLinear); + GLFunctions::glTexParameter(gl_const::GLMinFilter, gl_const::GLLinear); + GLFunctions::glTexParameter(gl_const::GLWrapT, gl_const::GLClampToEdge); + GLFunctions::glTexParameter(gl_const::GLWrapS, gl_const::GLClampToEdge); + + m_depthTextureId = GLFunctions::glGenTexture(); + GLFunctions::glBindTexture(m_depthTextureId); + GLFunctions::glTexImage2D(m_width, m_height, gl_const::GLDepthComponent, gl_const::GLUnsignedIntType, NULL); + + GLFunctions::glBindTexture(0); + + GLFunctions::glGenFramebuffer(&m_framebufferId); + GLFunctions::glBindFramebuffer(m_framebufferId); + + GLFunctions::glFramebufferTexture2D(gl_const::GLColorAttachment, m_colorTextureId); + GLFunctions::glFramebufferTexture2D(gl_const::GLDepthAttachment, m_depthTextureId); + GLFunctions::glFramebufferTexture2D(gl_const::GLStencilAttachment, 0); + + uint32_t const status = GLFunctions::glCheckFramebufferStatus(); + if (status != gl_const::GLFramebufferComplete) + LOG(LWARNING, ("Incomplete framebuffer:", strings::to_string(status))); + + m_defaultContext->setDefaultFramebuffer(); +} + +void Framebuffer::Enable() +{ + GLFunctions::glBindFramebuffer(m_framebufferId); +} + +void Framebuffer::Disable() +{ + ASSERT(m_defaultContext, ()); + m_defaultContext->setDefaultFramebuffer(); +} + +uint32_t Framebuffer::GetTextureId() const +{ + return m_colorTextureId; +} +} // namespace df diff --git a/drape_frontend/framebuffer.hpp b/drape_frontend/framebuffer.hpp new file mode 100644 index 0000000000..5091ad5d81 --- /dev/null +++ b/drape_frontend/framebuffer.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include "stdint.h" + +namespace dp +{ +class OGLContext; +} + +namespace df +{ + +class Framebuffer +{ +public: + Framebuffer() = default; + ~Framebuffer(); + + void SetDefaultContext(dp::OGLContext * context); + void SetSize(uint32_t width, uint32_t height); + + int32_t GetMaxSize(); + + void Enable(); + void Disable(); + + uint32_t GetTextureId() const; + +private: + void Destroy(); + + uint32_t m_width = 0; + uint32_t m_height = 0; + + uint32_t m_colorTextureId = 0; + uint32_t m_depthTextureId = 0; + uint32_t m_framebufferId = 0; + + dp::OGLContext * m_defaultContext = 0; + + int32_t m_maxTextureSize = 0; +}; + +} // namespace df diff --git a/drape_frontend/frontend_renderer.cpp b/drape_frontend/frontend_renderer.cpp index 4cf8c43987..b6eba50f70 100755 --- a/drape_frontend/frontend_renderer.cpp +++ b/drape_frontend/frontend_renderer.cpp @@ -1,11 +1,14 @@ #include "drape_frontend/animation/interpolation_holder.hpp" #include "drape_frontend/gui/drape_gui.hpp" +#include "drape_frontend/framebuffer.hpp" #include "drape_frontend/frontend_renderer.hpp" #include "drape_frontend/message_subclasses.hpp" +#include "drape_frontend/transparent_layer.hpp" #include "drape_frontend/visual_params.hpp" #include "drape_frontend/user_mark_shapes.hpp" #include "drape/debug_rect_renderer.hpp" +#include "drape/shader_def.hpp" #include "drape/support_manager.hpp" #include "drape/utils/glyph_usage_tracker.hpp" @@ -31,7 +34,7 @@ namespace df namespace { - +constexpr float kIsometryAngle = math::pi * 80.0f / 180.0f; const double VSyncInterval = 0.06; //const double VSyncInterval = 0.014; @@ -41,7 +44,12 @@ FrontendRenderer::FrontendRenderer(Params const & params) : BaseRenderer(ThreadsCommutator::RenderThread, params) , m_gpuProgramManager(new dp::GpuProgramManager()) , m_routeRenderer(new RouteRenderer()) + , m_framebuffer(new Framebuffer()) + , m_transparentLayer(new TransparentLayer()) , m_overlayTree(new dp::OverlayTree()) + , m_enablePerspectiveInNavigation(false) + , m_enable3dBuildings(false) + , m_isIsometry(false) , m_viewport(params.m_viewport) , m_userEventStream(params.m_isCountryLoadedFn) , m_modelViewChangedFn(params.m_modelViewChangedFn) @@ -130,8 +138,13 @@ void FrontendRenderer::AcceptMessage(ref_ptr message) TileKey const & key = msg->GetKey(); drape_ptr bucket = msg->AcceptBuffer(); ref_ptr program = m_gpuProgramManager->GetProgram(state.GetProgramIndex()); - program->Bind(); - bucket->GetBuffer()->Build(program); + ref_ptr program3d = m_gpuProgramManager->GetProgram(state.GetProgram3dIndex()); + bool const isPerspective = m_userEventStream.GetCurrentScreen().isPerspective(); + if (isPerspective) + program3d->Bind(); + else + program->Bind(); + bucket->GetBuffer()->Build(isPerspective ? program3d : program); if (!IsUserMarkLayer(key)) { if (CheckTileGenerations(key)) @@ -140,7 +153,7 @@ void FrontendRenderer::AcceptMessage(ref_ptr message) else { m_userMarkRenderGroups.emplace_back(make_unique_dp(state, key, move(bucket))); - m_userMarkRenderGroups.back()->SetRenderParams(program, make_ref(&m_generalUniforms)); + m_userMarkRenderGroups.back()->SetRenderParams(program, program3d, make_ref(&m_generalUniforms)); } break; } @@ -186,7 +199,7 @@ void FrontendRenderer::AcceptMessage(ref_ptr message) MessagePriority::High); blocker.Wait(); - m_requestedTiles->Set(screen, move(tiles)); + m_requestedTiles->Set(screen, m_isIsometry || screen.isPerspective(), move(tiles)); m_commutator->PostMessage(ThreadsCommutator::ResourceUploadThread, make_unique_dp(), MessagePriority::UberHighSingleton); @@ -301,7 +314,9 @@ void FrontendRenderer::AcceptMessage(ref_ptr message) case Message::FindVisiblePOI: { ref_ptr msg = message; - msg->SetFeatureID(GetVisiblePOI(m_userEventStream.GetCurrentScreen().GtoP(msg->GetPoint()))); + ScreenBase const & screen = m_userEventStream.GetCurrentScreen(); + msg->SetFeatureID(GetVisiblePOI(screen.isPerspective() ? screen.PtoP3d(screen.GtoP(msg->GetPoint())) + : screen.GtoP(msg->GetPoint()))); break; } @@ -313,10 +328,21 @@ void FrontendRenderer::AcceptMessage(ref_ptr message) break; if (msg->IsDismiss()) + { m_selectionShape->Hide(); + } else - m_selectionShape->Show(msg->GetSelectedObject(), msg->GetPosition(), msg->IsAnim()); - + { + double offsetZ = 0.0; + if (m_userEventStream.GetCurrentScreen().isPerspective()) + { + dp::OverlayTree::TSelectResult selectResult; + m_overlayTree->Select(msg->GetPosition(), selectResult); + for (ref_ptr handle : selectResult) + offsetZ = max(offsetZ, handle->GetPivotZ()); + } + m_selectionShape->Show(msg->GetSelectedObject(), msg->GetPosition(), offsetZ, msg->IsAnim()); + } break; } @@ -376,13 +402,36 @@ void FrontendRenderer::AcceptMessage(ref_ptr message) ref_ptr msg = message; m_routeRenderer->Clear(); if (msg->NeedDeactivateFollowing()) + { m_myPositionController->DeactivateRouting(); + m_overlayTree->SetFollowingMode(false); + if (m_enablePerspectiveInNavigation) + DisablePerspective(); + } + break; + } + + case Message::FollowRoute: + { + ref_ptr const msg = message; + m_myPositionController->NextMode(!m_enablePerspectiveInNavigation ? msg->GetPreferredZoomLevel() + : msg->GetPreferredZoomLevelIn3d()); + m_overlayTree->SetFollowingMode(true); + if (m_enablePerspectiveInNavigation) + { + bool immediatelyStart = !m_myPositionController->IsRotationActive(); + AddUserEvent(EnablePerspectiveEvent(msg->GetRotationAngle(), msg->GetAngleFOV(), + true /* animated */, immediatelyStart)); + } break; } case Message::DeactivateRouteFollowing: { m_myPositionController->DeactivateRouting(); + m_overlayTree->SetFollowingMode(false); + if (m_enablePerspectiveInNavigation) + DisablePerspective(); break; } @@ -448,7 +497,7 @@ void FrontendRenderer::AcceptMessage(ref_ptr message) } // Request new tiles. - m_requestedTiles->Set(screen, move(tiles)); + m_requestedTiles->Set(screen, m_isIsometry || screen.isPerspective(), move(tiles)); m_commutator->PostMessage(ThreadsCommutator::ResourceUploadThread, make_unique_dp(), MessagePriority::UberHighSingleton); @@ -458,6 +507,47 @@ void FrontendRenderer::AcceptMessage(ref_ptr message) break; } + case Message::EnablePerspective: + { + ref_ptr const msg = message; + AddUserEvent(EnablePerspectiveEvent(msg->GetRotationAngle(), msg->GetAngleFOV(), + false /* animated */, true /* immediately start */)); + break; + } + + case Message::Allow3dMode: + { + ref_ptr const msg = message; + bool const isPerspective = m_userEventStream.GetCurrentScreen().isPerspective(); +#ifdef OMIM_OS_DESKTOP + if (m_enablePerspectiveInNavigation == msg->AllowPerspective() && + m_enablePerspectiveInNavigation != isPerspective) + { + if (m_enablePerspectiveInNavigation) + AddUserEvent(EnablePerspectiveEvent(msg->GetRotationAngle(), msg->GetAngleFOV(), + false /* animated */, true /* immediately start */)); + else + AddUserEvent(DisablePerspectiveEvent()); + } +#endif + m_enablePerspectiveInNavigation = msg->AllowPerspective(); + m_enable3dBuildings = msg->Allow3dBuildings(); + + if (m_myPositionController->IsInRouting()) + { + if (m_enablePerspectiveInNavigation && !isPerspective && !m_perspectiveDiscarded) + { + AddUserEvent(EnablePerspectiveEvent(msg->GetRotationAngle(), msg->GetAngleFOV(), + true /* animated */, true /* immediately start */)); + } + else if (!m_enablePerspectiveInNavigation && (isPerspective || m_perspectiveDiscarded)) + { + DisablePerspective(); + } + } + break; + } + case Message::Invalidate: { // Do nothing here, new frame will be rendered because of this message processing. @@ -476,10 +566,17 @@ unique_ptr FrontendRenderer::CreateRoutine() void FrontendRenderer::OnResize(ScreenBase const & screen) { - m_viewport.SetViewport(0, 0, screen.GetWidth(), screen.GetHeight()); - m_myPositionController->SetPixelRect(screen.PixelRect()); - m_contextFactory->getDrawContext()->resize(m_viewport.GetWidth(), m_viewport.GetHeight()); - RefreshProjection(); + m2::RectD const viewportRect = screen.isPerspective() ? screen.PixelRectIn3d() : screen.PixelRect(); + + m_myPositionController->UpdatePixelPosition(screen); + m_myPositionController->SetPixelRect(viewportRect); + + m_viewport.SetViewport(0, 0, viewportRect.SizeX(), viewportRect.SizeY()); + m_contextFactory->getDrawContext()->resize(viewportRect.SizeX(), viewportRect.SizeY()); + RefreshProjection(screen); + RefreshPivotTransform(screen); + + m_framebuffer->SetSize(viewportRect.SizeX(), viewportRect.SizeY()); } void FrontendRenderer::AddToRenderGroup(vector> & groups, @@ -489,7 +586,9 @@ void FrontendRenderer::AddToRenderGroup(vector> & groups, { drape_ptr group = make_unique_dp(state, newTile); ref_ptr program = m_gpuProgramManager->GetProgram(state.GetProgramIndex()); - group->SetRenderParams(program, make_ref(&m_generalUniforms)); + ref_ptr program3d = m_gpuProgramManager->GetProgram(state.GetProgram3dIndex()); + + group->SetRenderParams(program, program3d, make_ref(&m_generalUniforms)); group->AddBucket(move(renderBucket)); groups.push_back(move(group)); } @@ -595,7 +694,7 @@ FeatureID FrontendRenderer::GetVisiblePOI(m2::RectD const & pixelRect) const ScreenBase const & screen = m_userEventStream.GetCurrentScreen(); for (ref_ptr handle : selectResult) { - double const curDist = pt.SquareLength(handle->GetPivot(screen)); + double const curDist = pt.SquareLength(handle->GetPivot(screen, screen.isPerspective())); if (curDist < dist) { dist = curDist; @@ -608,7 +707,7 @@ FeatureID FrontendRenderer::GetVisiblePOI(m2::RectD const & pixelRect) const void FrontendRenderer::BeginUpdateOverlayTree(ScreenBase const & modelView) { - if (m_overlayTree->Frame()) + if (m_overlayTree->Frame(modelView.isPerspective())) m_overlayTree->StartOverlayPlacing(modelView); } @@ -632,6 +731,8 @@ void FrontendRenderer::RenderScene(ScreenBase const & modelView) BeforeDrawFrame(); #endif + bool const isPerspective = modelView.isPerspective(); + RenderGroupComparator comparator; sort(m_renderGroups.begin(), m_renderGroups.end(), bind(&RenderGroupComparator::operator (), &comparator, _1, _2)); @@ -671,11 +772,26 @@ void FrontendRenderer::RenderScene(ScreenBase const & modelView) dp::GLState::DepthLayer prevLayer = dp::GLState::GeometryLayer; size_t currentRenderGroup = 0; + bool has3dAreas = false; + size_t area3dRenderGroupStart = 0; + size_t area3dRenderGroupEnd = 0; for (; currentRenderGroup < m_renderGroups.size(); ++currentRenderGroup) { drape_ptr const & group = m_renderGroups[currentRenderGroup]; dp::GLState const & state = group->GetState(); + + if ((isPerspective || m_isIsometry) && state.GetProgram3dIndex() == gpu::AREA_3D_PROGRAM) + { + if (!has3dAreas) + { + area3dRenderGroupStart = currentRenderGroup; + has3dAreas = true; + } + area3dRenderGroupEnd = currentRenderGroup; + continue; + } + dp::GLState::DepthLayer layer = state.GetDepthLayer(); if (prevLayer != layer && layer == dp::GLState::OverlayLayer) break; @@ -686,6 +802,7 @@ void FrontendRenderer::RenderScene(ScreenBase const & modelView) //GLFunctions::glClearDepth(); GLFunctions::glDisable(gl_const::GLDepthTest); + bool hasSelectedPOI = false; if (m_selectionShape != nullptr) { SelectionShape::ESelectedObject selectedObject = m_selectionShape->GetSelectedObject(); @@ -696,24 +813,53 @@ void FrontendRenderer::RenderScene(ScreenBase const & modelView) m_selectionShape->Render(modelView, make_ref(m_gpuProgramManager), m_generalUniforms); } else if (selectedObject == SelectionShape::OBJECT_POI) - m_selectionShape->Render(modelView, make_ref(m_gpuProgramManager), m_generalUniforms); + { + hasSelectedPOI = true; + if (!isPerspective) + m_selectionShape->Render(modelView, make_ref(m_gpuProgramManager), m_generalUniforms); + } } m_myPositionController->Render(MyPositionController::RenderAccuracy, modelView, make_ref(m_gpuProgramManager), m_generalUniforms); + if (has3dAreas) + { + m_framebuffer->Enable(); + GLFunctions::glClear(); + GLFunctions::glClearDepth(); + + GLFunctions::glEnable(gl_const::GLDepthTest); + for (size_t index = area3dRenderGroupStart; index <= area3dRenderGroupEnd; ++index) + { + drape_ptr const & group = m_renderGroups[index]; + if (group->GetState().GetProgram3dIndex() == gpu::AREA_3D_PROGRAM) + RenderSingleGroup(modelView, make_ref(group)); + } + m_framebuffer->Disable(); + + GLFunctions::glDisable(gl_const::GLDepthTest); + m_transparentLayer->Render(m_framebuffer->GetTextureId(), make_ref(m_gpuProgramManager)); + } + + if (isPerspective && hasSelectedPOI) + { + GLFunctions::glDisable(gl_const::GLDepthTest); + m_selectionShape->Render(modelView, make_ref(m_gpuProgramManager), m_generalUniforms); + } + + GLFunctions::glEnable(gl_const::GLDepthTest); + GLFunctions::glClearDepth(); for (; currentRenderGroup < m_renderGroups.size(); ++currentRenderGroup) { drape_ptr const & group = m_renderGroups[currentRenderGroup]; RenderSingleGroup(modelView, make_ref(group)); } - GLFunctions::glClearDepth(); + GLFunctions::glDisable(gl_const::GLDepthTest); if (m_selectionShape != nullptr && m_selectionShape->GetSelectedObject() == SelectionShape::OBJECT_USER_MARK) m_selectionShape->Render(modelView, make_ref(m_gpuProgramManager), m_generalUniforms); - GLFunctions::glDisable(gl_const::GLDepthTest); - m_routeRenderer->RenderRoute(modelView, make_ref(m_gpuProgramManager), m_generalUniforms); for (drape_ptr const & group : m_userMarkRenderGroups) @@ -729,7 +875,18 @@ void FrontendRenderer::RenderScene(ScreenBase const & modelView) modelView, make_ref(m_gpuProgramManager), m_generalUniforms); if (m_guiRenderer != nullptr) - m_guiRenderer->Render(make_ref(m_gpuProgramManager), modelView); + { + if (isPerspective) + { + ScreenBase modelView2d = modelView; + modelView2d.ResetPerspective(); + m_guiRenderer->Render(make_ref(m_gpuProgramManager), modelView2d); + } + else + { + m_guiRenderer->Render(make_ref(m_gpuProgramManager), modelView); + } + } GLFunctions::glEnable(gl_const::GLDepthTest); @@ -738,17 +895,22 @@ void FrontendRenderer::RenderScene(ScreenBase const & modelView) #endif } +bool FrontendRenderer::IsPerspective() const +{ + return m_userEventStream.GetCurrentScreen().isPerspective(); +} + void FrontendRenderer::RenderSingleGroup(ScreenBase const & modelView, ref_ptr group) { group->UpdateAnimation(); group->Render(modelView); } -void FrontendRenderer::RefreshProjection() +void FrontendRenderer::RefreshProjection(ScreenBase const & screen) { array m; - dp::MakeProjection(m, 0.0f, m_viewport.GetWidth(), m_viewport.GetHeight(), 0.0f); + dp::MakeProjection(m, 0.0f, screen.GetWidth(), screen.GetHeight(), 0.0f); m_generalUniforms.SetMatrix4x4Value("projection", m.data()); } @@ -765,13 +927,37 @@ void FrontendRenderer::RefreshModelView(ScreenBase const & screen) mv(3, 0) = m(0, 2); mv(3, 1) = m(1, 2); mv(3, 2) = 0; mv(3, 3) = m(2, 2); m_generalUniforms.SetMatrix4x4Value("modelView", mv.m_data); + + float const zScale = 2.0f / (screen.GetHeight() * screen.GetScale()); + m_generalUniforms.SetFloatValue("zScale", zScale); +} + +void FrontendRenderer::RefreshPivotTransform(ScreenBase const & screen) +{ + if (screen.isPerspective()) + { + math::Matrix transform(screen.Pto3dMatrix()); + m_generalUniforms.SetMatrix4x4Value("pivotTransform", transform.m_data); + } + else if (m_isIsometry) + { + math::Matrix transform(math::Identity()); + transform(2, 1) = -1.0f / tan(kIsometryAngle); + transform(2, 2) = 1.0f / screen.GetHeight(); + m_generalUniforms.SetMatrix4x4Value("pivotTransform", transform.m_data); + } + else + { + math::Matrix transform(math::Identity()); + m_generalUniforms.SetMatrix4x4Value("pivotTransform", transform.m_data); + } } void FrontendRenderer::RefreshBgColor() { uint32_t color = drule::rules().GetBgColor(df::GetDrawTileScale(m_userEventStream.GetCurrentScreen())); - dp::Color c = dp::Extract(color, 255 - (color >> 24)); - GLFunctions::glClearColor(c.GetRedF(), c.GetGreenF(), c.GetBlueF(), 1.0f); + dp::Color c = dp::Extract(color, 0 /*255 - (color >> 24)*/); + GLFunctions::glClearColor(c.GetRedF(), c.GetGreenF(), c.GetBlueF(), 0.0f); } int FrontendRenderer::GetCurrentZoomLevel() const @@ -785,9 +971,34 @@ int FrontendRenderer::GetCurrentZoomLevelForData() const return (m_currentZoomLevel <= upperScale ? m_currentZoomLevel : upperScale); } +void FrontendRenderer::DisablePerspective() +{ + m_perspectiveDiscarded = false; + AddUserEvent(DisablePerspectiveEvent()); +} + +void FrontendRenderer::CheckMinAllowableIn3dScale() +{ + bool const isScaleAllowableIn3d = UserEventStream::IsScaleAllowableIn3d(m_currentZoomLevel); + m_isIsometry = m_enable3dBuildings && isScaleAllowableIn3d; + + if (!m_enablePerspectiveInNavigation || m_userEventStream.IsInPerspectiveAnimation()) + return; + + bool const switchTo2d = !isScaleAllowableIn3d; + if ((!switchTo2d && !m_perspectiveDiscarded) || + (switchTo2d && !m_userEventStream.GetCurrentScreen().isPerspective())) + return; + + m_perspectiveDiscarded = switchTo2d; + AddUserEvent(SwitchViewModeEvent(switchTo2d)); +} + void FrontendRenderer::ResolveZoomLevel(ScreenBase const & screen) { m_currentZoomLevel = GetDrawTileScale(screen); + + CheckMinAllowableIn3dScale(); } void FrontendRenderer::OnTap(m2::PointD const & pt, bool isLongTap) @@ -799,7 +1010,10 @@ void FrontendRenderer::OnTap(m2::PointD const & pt, bool isLongTap) ScreenBase const & screen = m_userEventStream.GetCurrentScreen(); bool isMyPosition = false; if (m_myPositionController->IsModeHasPosition()) - isMyPosition = selectRect.IsPointInside(screen.GtoP(m_myPositionController->Position())); + { + m2::PointD const pt = screen.PtoP3d(screen.GtoP(m_myPositionController->Position())); + isMyPosition = selectRect.IsPointInside(pt); + } m_tapEventInfoFn(pt, isLongTap, isMyPosition, GetVisiblePOI(selectRect)); } @@ -940,6 +1154,7 @@ void FrontendRenderer::Routine::Do() dp::OGLContext * context = m_renderer.m_contextFactory->getDrawContext(); context->makeCurrent(); + m_renderer.m_framebuffer->SetDefaultContext(context); GLFunctions::Init(); GLFunctions::AttachCache(this_thread::get_id()); @@ -1058,6 +1273,8 @@ void FrontendRenderer::ReleaseResources() m_myPositionController.reset(); m_selectionShape.release(); m_routeRenderer.reset(); + m_framebuffer.reset(); + m_transparentLayer.reset(); m_gpuProgramManager.reset(); m_contextFactory->getDrawContext()->doneCurrent(); @@ -1100,6 +1317,7 @@ ScreenBase const & FrontendRenderer::ProcessEvents(bool & modelViewChanged, bool { ScreenBase const & modelView = m_userEventStream.ProcessEvents(modelViewChanged, viewportChanged); gui::DrapeGui::Instance().SetInUserAction(m_userEventStream.IsInUserAction()); + return modelView; } @@ -1107,6 +1325,9 @@ void FrontendRenderer::PrepareScene(ScreenBase const & modelView) { RefreshModelView(modelView); RefreshBgColor(); + RefreshPivotTransform(modelView); + + m_myPositionController->UpdatePixelPosition(modelView); } void FrontendRenderer::UpdateScene(ScreenBase const & modelView) @@ -1115,13 +1336,14 @@ void FrontendRenderer::UpdateScene(ScreenBase const & modelView) TTilesCollection tiles; ResolveTileKeys(modelView, tiles); + m_overlayTree->ForceUpdate(); auto removePredicate = [this](drape_ptr const & group) { return group->IsOverlay() && group->GetTileKey().m_styleZoomLevel > GetCurrentZoomLevel(); }; RemoveRenderGroups(removePredicate); - m_requestedTiles->Set(modelView, move(tiles)); + m_requestedTiles->Set(modelView, m_isIsometry || modelView.isPerspective(), move(tiles)); m_commutator->PostMessage(ThreadsCommutator::ResourceUploadThread, make_unique_dp(), MessagePriority::UberHighSingleton); diff --git a/drape_frontend/frontend_renderer.hpp b/drape_frontend/frontend_renderer.hpp index c6f11180e6..352c2744dd 100755 --- a/drape_frontend/frontend_renderer.hpp +++ b/drape_frontend/frontend_renderer.hpp @@ -46,6 +46,8 @@ namespace df { class SelectionShape; +class Framebuffer; +class TransparentLayer; struct TapInfo { @@ -136,8 +138,9 @@ private: void OnResize(ScreenBase const & screen); void RenderScene(ScreenBase const & modelView); void RenderSingleGroup(ScreenBase const & modelView, ref_ptr group); - void RefreshProjection(); + void RefreshProjection(ScreenBase const & screen); void RefreshModelView(ScreenBase const & screen); + void RefreshPivotTransform(ScreenBase const & screen); void RefreshBgColor(); ScreenBase const & ProcessEvents(bool & modelViewChanged, bool & viewportChanged); @@ -151,6 +154,9 @@ private: int GetCurrentZoomLevel() const; int GetCurrentZoomLevelForData() const; void ResolveZoomLevel(ScreenBase const & screen); + void CheckMinAllowableIn3dScale(); + + void DisablePerspective(); void OnTap(m2::PointD const & pt, bool isLong) override; void OnForceTap(m2::PointD const & pt) override; @@ -207,6 +213,8 @@ private: FeatureID GetVisiblePOI(m2::PointD const & pixelPoint) const; FeatureID GetVisiblePOI(m2::RectD const & pixelRect) const; + bool IsPerspective() const; + private: drape_ptr m_gpuProgramManager; @@ -219,11 +227,17 @@ private: drape_ptr m_myPositionController; drape_ptr m_selectionShape; drape_ptr m_routeRenderer; + drape_ptr m_framebuffer; + drape_ptr m_transparentLayer; drape_ptr m_overlayTree; dp::UniformValuesStorage m_generalUniforms; + bool m_enablePerspectiveInNavigation; + bool m_enable3dBuildings; + bool m_isIsometry; + Viewport m_viewport; UserEventStream m_userEventStream; TModelViewChanged m_modelViewChangedFn; @@ -232,6 +246,9 @@ private: unique_ptr m_tileTree; int m_currentZoomLevel = -1; + + bool m_perspectiveDiscarded = false; + ref_ptr m_requestedTiles; uint64_t m_maxGeneration; diff --git a/drape_frontend/gui/gui_text.cpp b/drape_frontend/gui/gui_text.cpp index ce902bd3d6..b6eebf9781 100644 --- a/drape_frontend/gui/gui_text.cpp +++ b/drape_frontend/gui/gui_text.cpp @@ -105,7 +105,7 @@ dp::BindingInfo const & StaticLabel::Vertex::GetBindingInfo() return *info.get(); } -StaticLabel::LabelResult::LabelResult() : m_state(gpu::TEXT_OUTLINED_PROGRAM, dp::GLState::Gui) {} +StaticLabel::LabelResult::LabelResult() : m_state(gpu::TEXT_OUTLINED_GUI_PROGRAM, dp::GLState::Gui) {} char const * StaticLabel::DefaultDelim = "\n"; @@ -281,7 +281,7 @@ dp::BindingInfo const & MutableLabel::DynamicVertex::GetBindingInfo() return *info.get(); } -MutableLabel::PrecacheResult::PrecacheResult() : m_state(gpu::TEXT_OUTLINED_PROGRAM, dp::GLState::Gui) {} +MutableLabel::PrecacheResult::PrecacheResult() : m_state(gpu::TEXT_OUTLINED_GUI_PROGRAM, dp::GLState::Gui) {} MutableLabel::MutableLabel(dp::Anchor anchor) : m_anchor(anchor) diff --git a/drape_frontend/gui/shape.cpp b/drape_frontend/gui/shape.cpp index f1e4919623..cd0f28bdce 100644 --- a/drape_frontend/gui/shape.cpp +++ b/drape_frontend/gui/shape.cpp @@ -10,7 +10,7 @@ namespace gui { Handle::Handle(dp::Anchor anchor, const m2::PointF & pivot, const m2::PointF & size) - : dp::OverlayHandle(FeatureID(), anchor, 0) + : dp::OverlayHandle(FeatureID(), anchor, 0, false) , m_pivot(glsl::ToVec2(pivot)), m_size(size) { } @@ -38,15 +38,18 @@ bool Handle::IndexesRequired() const return false; } -m2::RectD Handle::GetPixelRect(const ScreenBase & screen) const +m2::RectD Handle::GetPixelRect(const ScreenBase & screen, bool perspective) const { + // There is no need to check intersection of gui elements. return m2::RectD(); } -void Handle::GetPixelShape(const ScreenBase & screen, dp::OverlayHandle::Rects & rects) const +void Handle::GetPixelShape(const ScreenBase & screen, dp::OverlayHandle::Rects & rects, bool perspective) const { + // There is no need to check intersection of gui elements. UNUSED_VALUE(screen); UNUSED_VALUE(rects); + UNUSED_VALUE(perspective); } bool TappableHandle::IsTapped(m2::RectD const & touchArea) const diff --git a/drape_frontend/gui/shape.hpp b/drape_frontend/gui/shape.hpp index 55db04fc7e..98d5f517f6 100644 --- a/drape_frontend/gui/shape.hpp +++ b/drape_frontend/gui/shape.hpp @@ -28,8 +28,8 @@ public: virtual void OnTapEnd(){} virtual bool IndexesRequired() const override; - virtual m2::RectD GetPixelRect(ScreenBase const & screen) const override; - virtual void GetPixelShape(ScreenBase const & screen, Rects & rects) const override; + virtual m2::RectD GetPixelRect(ScreenBase const & screen, bool perspective) const override; + virtual void GetPixelShape(ScreenBase const & screen, Rects & rects, bool perspective) const override; m2::PointF GetSize() const { return m_size; } virtual void SetPivot(glsl::vec2 const & pivot) { m_pivot = pivot; } diff --git a/drape_frontend/memory_feature_index.hpp b/drape_frontend/memory_feature_index.hpp index ef91257c7f..6d70dee9e5 100644 --- a/drape_frontend/memory_feature_index.hpp +++ b/drape_frontend/memory_feature_index.hpp @@ -33,8 +33,10 @@ struct FeatureInfo bool m_isOwner; }; -size_t const AverageFeaturesCount = 2048; -using TFeaturesInfo = buffer_vector; +// It is better for TileInfo to have size that is equal or slightly less +// than several memory pages. +size_t const kAverageFeaturesCount = 2040; +using TFeaturesInfo = buffer_vector; class MemoryFeatureIndex : private noncopyable { diff --git a/drape_frontend/message.hpp b/drape_frontend/message.hpp index 534c5e2cb1..0c5fafe824 100644 --- a/drape_frontend/message.hpp +++ b/drape_frontend/message.hpp @@ -39,10 +39,14 @@ public: RemoveRoute, FlushRoute, FlushRouteSign, + FollowRoute, DeactivateRouteFollowing, UpdateMapStyle, InvalidateTextures, - Invalidate + Invalidate, + Allow3dMode, + Allow3dBuildings, + EnablePerspective }; virtual ~Message() {} diff --git a/drape_frontend/message_subclasses.hpp b/drape_frontend/message_subclasses.hpp index ab074ecb77..3f1a1dd814 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 const m_preferredZoomLevel; + int const m_preferredZoomLevelIn3d; + double const m_rotationAngle; + double const m_angleFOV; +}; + class InvalidateTexturesMessage : public BaseBlockingMessage { public: @@ -630,4 +653,59 @@ public: Type GetType() const override { return Message::DeactivateRouteFollowing; } }; +class Allow3dModeMessage : public Message +{ +public: + Allow3dModeMessage(bool allowPerspective, bool allow3dBuildings, double rotationAngle, double angleFOV) + : m_allowPerspective(allowPerspective) + , m_allow3dBuildings(allow3dBuildings) + , m_rotationAngle(rotationAngle) + , m_angleFOV(angleFOV) + {} + + Type GetType() const override { return Message::Allow3dMode; } + bool AllowPerspective() const { return m_allowPerspective; } + bool Allow3dBuildings() const { return m_allow3dBuildings; } + double GetRotationAngle() const { return m_rotationAngle; } + double GetAngleFOV() const { return m_angleFOV; } + +private: + bool const m_allowPerspective; + bool const m_allow3dBuildings; + double const m_rotationAngle; + double const m_angleFOV; +}; + +class Allow3dBuildingsMessage : public Message +{ +public: + Allow3dBuildingsMessage(bool allow3dBuildings) + : m_allow3dBuildings(allow3dBuildings) + {} + + Type GetType() const override { return Message::Allow3dBuildings; } + bool Allow3dBuildings() const { return m_allow3dBuildings; } + +private: + bool const m_allow3dBuildings; +}; + +class EnablePerspectiveMessage : public Message +{ +public: + EnablePerspectiveMessage(double rotationAngle, double angleFOV) + : m_rotationAngle(rotationAngle) + , m_angleFOV(angleFOV) + {} + + Type GetType() const override { return Message::EnablePerspective; } + + double GetRotationAngle() const { return m_rotationAngle; } + double GetAngleFOV() const { return m_angleFOV; } + +private: + double const m_rotationAngle; + double const m_angleFOV; +}; + } // namespace df diff --git a/drape_frontend/my_position.cpp b/drape_frontend/my_position.cpp index 996edbd21a..35754ad293 100644 --- a/drape_frontend/my_position.cpp +++ b/drape_frontend/my_position.cpp @@ -103,12 +103,22 @@ void MyPosition::RenderMyPosition(ScreenBase const & screen, ref_ptr mng, dp::UniformValuesStorage const & commonUniforms) { - dp::UniformValuesStorage uniforms = commonUniforms; - uniforms.SetFloatValue("u_position", m_position.x, m_position.y, dp::depth::MY_POSITION_MARK); - uniforms.SetFloatValue("u_azimut", -(m_azimuth + screen.GetAngle())); - uniforms.SetFloatValue("u_opacity", 1.0); - RenderPart(mng, uniforms, (m_showAzimuth == true) ? - (m_isRoutingMode ? MY_POSITION_ROUTING_ARROW : MY_POSITION_ARROW) : MY_POSITION_POINT); + if (screen.isPerspective() && m_isRoutingMode && m_showAzimuth) + { + m_arrow3d.SetPosition(m_position); + m_arrow3d.SetAzimuth(m_azimuth); + + m_arrow3d.Render(screen, mng); + } + else + { + dp::UniformValuesStorage uniforms = commonUniforms; + uniforms.SetFloatValue("u_position", m_position.x, m_position.y, dp::depth::MY_POSITION_MARK); + uniforms.SetFloatValue("u_azimut", -(m_azimuth + screen.GetAngle())); + uniforms.SetFloatValue("u_opacity", 1.0); + RenderPart(mng, uniforms, (m_showAzimuth == true) ? + (m_isRoutingMode ? MY_POSITION_ROUTING_ARROW : MY_POSITION_ARROW) : MY_POSITION_POINT); + } } void MyPosition::CacheAccuracySector(ref_ptr mng) @@ -188,6 +198,8 @@ void MyPosition::CachePointPosition(ref_ptr mng) m2::RectF const & routingArrowTexRect = routingArrowSymbol.GetTexRect(); m2::PointF routingArrowHalfSize = m2::PointF(routingArrowSymbol.GetPixelSize()) * 0.5f; + m_arrow3d.SetSize(routingArrowSymbol.GetPixelSize().x, routingArrowSymbol.GetPixelSize().y); + Vertex routingArrowData[4]= { { glsl::vec2(-routingArrowHalfSize.x, routingArrowHalfSize.y), glsl::ToVec2(routingArrowTexRect.LeftTop()) }, diff --git a/drape_frontend/my_position.hpp b/drape_frontend/my_position.hpp index 7bd79231ee..27d1f2a95c 100644 --- a/drape_frontend/my_position.hpp +++ b/drape_frontend/my_position.hpp @@ -1,5 +1,6 @@ #pragma once +#include "arrow3d.hpp" #include "render_node.hpp" #include "drape/vertex_array_buffer.hpp" @@ -63,6 +64,8 @@ private: vector m_parts; vector m_nodes; + + Arrow3d m_arrow3d; }; } diff --git a/drape_frontend/my_position_controller.cpp b/drape_frontend/my_position_controller.cpp index 8426f47d9e..32e74ba27e 100644 --- a/drape_frontend/my_position_controller.cpp +++ b/drape_frontend/my_position_controller.cpp @@ -17,6 +17,7 @@ namespace { int const POSITION_Y_OFFSET = 75; +int const POSITION_Y_OFFSET_3D = 80; double const GPS_BEARING_LIFETIME_S = 5.0; double const MIN_SPEED_THRESHOLD_MPS = 1.0; @@ -93,8 +94,10 @@ MyPositionController::MyPositionController(location::EMyPositionMode initMode) , m_position(m2::PointD::Zero()) , m_drawDirection(0.0) , m_lastGPSBearing(false) + , m_positionYOffset(POSITION_Y_OFFSET) , m_isVisible(false) , m_isDirtyViewport(false) + , m_needAnimation(false) { if (initMode > location::MODE_UNKNOWN_POSITION) m_afterPendingMode = initMode; @@ -113,6 +116,12 @@ void MyPositionController::SetPixelRect(m2::RectD const & pixelRect) Follow(); } +void MyPositionController::UpdatePixelPosition(ScreenBase const & screen) +{ + m_positionYOffset = screen.isPerspective() ? POSITION_Y_OFFSET_3D : POSITION_Y_OFFSET; + m_pixelPositionRaF = screen.P3dtoP(GetRaFPixelBinding()); +} + void MyPositionController::SetListener(ref_ptr listener) { m_listener = listener; @@ -148,6 +157,8 @@ void MyPositionController::DragEnded(m2::PointD const & distance) SetModeInfo(ResetModeBit(m_modeInfo, BlockAnimation)); if (distance.Length() > 0.2 * min(m_pixelRect.SizeX(), m_pixelRect.SizeY())) StopLocationFollow(); + else if (IsModeChangeViewport()) + m_needAnimation = true; Follow(); } @@ -194,6 +205,10 @@ void MyPositionController::ScaleEnded() SetModeInfo(ResetModeBit(m_modeInfo, StopFollowOnActionEnd)); StopLocationFollow(); } + else if (IsModeChangeViewport()) + { + m_needAnimation = true; + } Follow(); } @@ -318,7 +333,14 @@ void MyPositionController::Render(uint32_t renderMode, ScreenBase const & screen m_isDirtyViewport = false; } - m_shape->SetPosition(GetDrawablePosition()); + bool const fixedPixelPos = IsModeChangeViewport() && !TestModeBit(m_modeInfo, BlockAnimation) && + !m_needAnimation && !(m_anim != nullptr && m_anim->IsMovingActive()); + + if (fixedPixelPos) + m_shape->SetPosition(screen.PtoG(screen.P3dtoP(GetCurrentPixelBinding()))); + else + m_shape->SetPosition(GetDrawablePosition()); + m_shape->SetAzimuth(GetDrawableAzimut()); m_shape->SetIsValidAzimuth(IsRotationActive()); m_shape->SetAccuracy(m_errorRadius); @@ -393,9 +415,13 @@ void MyPositionController::Assign(location::GpsInfo const & info, bool isNavigab if (m_listener) m_listener->PositionChanged(Position()); - if (!AlmostCurrentPosition(oldPos) || !AlmostCurrentAzimut(oldAzimut) ) + if (!AlmostCurrentPosition(oldPos) || !AlmostCurrentAzimut(oldAzimut)) { - CreateAnim(oldPos, oldAzimut, screen); + if (m_needAnimation || !IsModeChangeViewport()) + { + CreateAnim(oldPos, oldAzimut, screen); + m_needAnimation = false; + } m_isDirtyViewport = true; } } @@ -508,13 +534,13 @@ void MyPositionController::Follow(int preferredZoomLevel) if (currentMode == location::MODE_FOLLOW) ChangeModelView(m_position); else if (currentMode == location::MODE_ROTATE_AND_FOLLOW) - ChangeModelView(m_position, m_drawDirection, GetRaFPixelBinding(), preferredZoomLevel); + ChangeModelView(m_position, m_drawDirection, m_pixelPositionRaF, preferredZoomLevel); } m2::PointD MyPositionController::GetRaFPixelBinding() const { - return m2::PointD (m_pixelRect.Center().x, - m_pixelRect.maxY() - POSITION_Y_OFFSET * VisualParams::Instance().GetVisualScale()); + return m2::PointD(m_pixelRect.Center().x, + m_pixelRect.maxY() - m_positionYOffset * VisualParams::Instance().GetVisualScale()); } m2::PointD MyPositionController::GetCurrentPixelBinding() const diff --git a/drape_frontend/my_position_controller.hpp b/drape_frontend/my_position_controller.hpp index 2628f295a6..f139170121 100644 --- a/drape_frontend/my_position_controller.hpp +++ b/drape_frontend/my_position_controller.hpp @@ -44,6 +44,7 @@ public: ~MyPositionController(); void SetPixelRect(m2::RectD const & pixelRect); + void UpdatePixelPosition(ScreenBase const & screen); void SetListener(ref_ptr listener); m2::PointD const & Position() const; @@ -84,6 +85,8 @@ public: dp::UniformValuesStorage const & commonUniforms); bool IsFollowingActive() const; + bool IsRotationActive() const; + bool IsInRouting() const; private: void AnimateStateTransition(location::EMyPositionMode oldMode, location::EMyPositionMode newMode); @@ -96,9 +99,6 @@ private: location::EMyPositionMode GetMode() const; void CallModeListener(uint32_t mode); - bool IsInRouting() const; - bool IsRotationActive() const; - bool IsVisible() const { return m_isVisible; } void SetIsVisible(bool isVisible) { m_isVisible = isVisible; } @@ -143,9 +143,12 @@ private: my::HighResTimer m_lastGPSBearing; m2::RectD m_pixelRect; + m2::PointD m_pixelPositionRaF; + double m_positionYOffset; bool m_isVisible; bool m_isDirtyViewport; + bool m_needAnimation; class MyPositionAnim; mutable drape_ptr m_anim; diff --git a/drape_frontend/navigator.cpp b/drape_frontend/navigator.cpp index 29927161a9..356297aad1 100644 --- a/drape_frontend/navigator.cpp +++ b/drape_frontend/navigator.cpp @@ -64,10 +64,12 @@ void Navigator::SetFromRect(m2::AnyRectD const & r, uint32_t tileSize, double vi tmp.SetFromRect(r); tmp = ScaleInto(tmp, worldR); if (CheckMaxScale(tmp, tileSize, visualScale)) + { m_Screen = tmp; - if (!m_InAction) - m_StartScreen = tmp; + if (!m_InAction) + m_StartScreen = tmp; + } } void Navigator::CenterViewport(m2::PointD const & p) @@ -107,11 +109,25 @@ void Navigator::OnSize(int w, int h) { m2::RectD const & worldR = df::GetWorldRect(); + double const fov = m_Screen.GetAngleFOV(); + double const rotation = m_Screen.GetRotationAngle(); + if (m_Screen.isPerspective()) + { + m_Screen.ResetPerspective(); + m_StartScreen.ResetPerspective(); + } + m_Screen.OnSize(0, 0, w, h); m_Screen = ShrinkAndScaleInto(m_Screen, worldR); m_StartScreen.OnSize(0, 0, w, h); m_StartScreen = ShrinkAndScaleInto(m_StartScreen, worldR); + + if (fov != 0.0) + { + m_Screen.ApplyPerspective(rotation, rotation, fov); + m_StartScreen.ApplyPerspective(rotation, rotation, fov); + } } m2::PointD Navigator::GtoP(m2::PointD const & pt) const @@ -124,6 +140,11 @@ m2::PointD Navigator::PtoG(m2::PointD const & pt) const return m_Screen.PtoG(pt); } +m2::PointD Navigator::P3dtoP(m2::PointD const & pt) const +{ + return m_Screen.P3dtoP(pt); +} + bool Navigator::CanShrinkInto(ScreenBase const & screen, m2::RectD const & boundRect) { m2::RectD clipRect = screen.ClipRect(); @@ -379,8 +400,10 @@ void Navigator::Scale(m2::PointD const & pt, double factor) void Navigator::CalculateScale(m2::PointD const & pt, double factor, ScreenBase & screen) { m2::PointD startPt, endPt; - CalcScalePoints(pt, factor, screen.PixelRect(), startPt, endPt); - ScaleImpl(pt, endPt, pt, startPt, factor > 1, false, screen); + CalcScalePoints(pt, factor, screen.isPerspective() ? screen.PixelRectIn3d() : screen.PixelRect(), startPt, endPt); + m2::PointD const newOffset = (endPt - pt) / 2.0; + m2::PointD const oldOffset = (startPt - pt) / 2.0; + ScaleImpl(pt - newOffset, pt + newOffset, pt - oldOffset, pt + oldOffset, factor > 1, false, screen); } bool Navigator::CheckMinScale(ScreenBase const & screen) const @@ -415,9 +438,13 @@ bool Navigator::ScaleImpl(m2::PointD const & newPt1, m2::PointD const & newPt2, bool skipMinScaleAndBordersCheck, bool doRotateScreen, ScreenBase & screen) { + m2::PointD const center3d = oldPt1; + m2::PointD const center2d = screen.P3dtoP(center3d); + m2::PointD const offset = center2d - center3d; math::Matrix const newM = - screen.GtoPMatrix() * ScreenBase::CalcTransform(oldPt1, oldPt2, - newPt1, newPt2, doRotateScreen); + screen.GtoPMatrix() * ScreenBase::CalcTransform(oldPt1 + offset, oldPt2 + offset, + newPt1 + offset, newPt2 + offset, + doRotateScreen); ScreenBase tmp = screen; tmp.SetGtoPMatrix(newM); @@ -510,6 +537,23 @@ bool Navigator::IsRotatingDuringScale() const return m_IsRotatingDuringScale; } +void Navigator::Enable3dMode(double currentRotationAngle, double maxRotationAngle, double angleFOV) +{ + ASSERT(!m_Screen.isPerspective(), ()); + m_Screen.ApplyPerspective(currentRotationAngle, maxRotationAngle, angleFOV); +} + +void Navigator::SetRotationIn3dMode(double rotationAngle) +{ + m_Screen.SetRotationAngle(rotationAngle); +} + +void Navigator::Disable3dMode() +{ + ASSERT(m_Screen.isPerspective(), ()); + m_Screen.ResetPerspective(); +} + m2::AnyRectD ToRotated(Navigator const & navigator, m2::RectD const & rect) { double const dx = rect.SizeX(); diff --git a/drape_frontend/navigator.hpp b/drape_frontend/navigator.hpp index 12aa815016..d527a09303 100644 --- a/drape_frontend/navigator.hpp +++ b/drape_frontend/navigator.hpp @@ -32,6 +32,7 @@ public: m2::PointD GtoP(m2::PointD const & pt) const; m2::PointD PtoG(m2::PointD const & pt) const; + m2::PointD P3dtoP(m2::PointD const & pt) const; void StartDrag(m2::PointD const & pt); void DoDrag(m2::PointD const & pt); @@ -47,6 +48,10 @@ public: void CalculateScale(m2::PointD const & pt, double factor, ScreenBase & screen); bool InAction() const; + void Enable3dMode(double currentRotationAngle, double maxRotationAngle, double angleFOV); + void SetRotationIn3dMode(double rotationAngle); + void Disable3dMode(); + private: bool CheckMinScale(ScreenBase const & screen) const; bool CheckMaxScale(ScreenBase const & screen) const; @@ -63,6 +68,7 @@ private: ScreenBase m_StartScreen; // Internal screen to do GtoP() and PtoG() calculations. It is always up to date with navigation. ScreenBase m_Screen; + // Intial point for dragging and scaling. m2::PointD m_StartPt1; // Last point for dragging and scaling. diff --git a/drape_frontend/path_symbol_shape.cpp b/drape_frontend/path_symbol_shape.cpp index d65ec00590..896b3b6c5a 100644 --- a/drape_frontend/path_symbol_shape.cpp +++ b/drape_frontend/path_symbol_shape.cpp @@ -48,10 +48,10 @@ void PathSymbolShape::Draw(ref_ptr batcher, ref_ptr textureManager) - : TextHandle(FeatureID(), layout->GetText(), dp::Center, priority, textureManager) + float mercatorOffset, float depth, + uint32_t textIndex, uint64_t priority, + uint64_t priorityFollowingMode, + ref_ptr textureManager, + bool isBillboard) + : TextHandle(FeatureID(), layout->GetText(), dp::Center, priority, textureManager, isBillboard) , m_spline(spl) , m_layout(layout) + , m_textIndex(textIndex) + , m_globalOffset(mercatorOffset) + , m_depth(depth) + , m_priorityFollowingMode(priorityFollowingMode) { - m_centerPointIter = m_spline.CreateIterator(); - m_centerPointIter.Advance(mercatorOffset); + + m2::Spline::iterator centerPointIter = m_spline.CreateIterator(); + centerPointIter.Advance(m_globalOffset); + m_globalPivot = centerPointIter.m_pos; + m_buffer.resize(4 * m_layout->GetGlyphCount()); } bool Update(ScreenBase const & screen) override { if (!df::TextHandle::Update(screen)) return false; + + vector const & globalPoints = m_spline->GetPath(); + m2::Spline pixelSpline(m_spline->GetSize()); + m2::Spline::iterator centerPointIter; - if (m_normals.empty()) - m_normals.resize(4 * m_layout->GetGlyphCount()); + if (screen.isPerspective()) + { + float pixelOffset = 0.0f; + uint32_t startIndex = 0; + bool foundOffset = false; + for (auto pos : globalPoints) + { + pos = screen.GtoP(pos); + if (!screen.PixelRect().IsPointInside(pos)) + { + if (foundOffset = CalculateOffsets(pixelSpline, startIndex, pixelOffset)) + break; - return m_layout->CacheDynamicGeometry(m_centerPointIter, screen, m_normals); + pixelSpline = m2::Spline(m_spline->GetSize()); + continue; + } + pixelSpline.AddPoint(screen.PtoP3d(pos)); + } + + if (!foundOffset && !CalculateOffsets(pixelSpline, startIndex, pixelOffset)) + return false; + + centerPointIter.Attach(pixelSpline); + centerPointIter.Advance(pixelOffset); + m_globalPivot = screen.PtoG(screen.P3dtoP(centerPointIter.m_pos)); + } + else + { + for (auto const & pos : globalPoints) + pixelSpline.AddPoint(screen.GtoP(pos)); + + centerPointIter.Attach(pixelSpline); + centerPointIter.Advance(m_globalOffset / screen.GetScale()); + m_globalPivot = screen.PtoG(centerPointIter.m_pos); + } + + if (m_buffer.empty()) + m_buffer.resize(4 * m_layout->GetGlyphCount()); + return m_layout->CacheDynamicGeometry(centerPointIter, m_depth, m_globalPivot, m_buffer); } - m2::RectD GetPixelRect(ScreenBase const & screen) const override + m2::RectD GetPixelRect(ScreenBase const & screen, bool perspective) const override { - m2::PointD const pixelPivot(screen.GtoP(m_centerPointIter.m_pos)); + m2::PointD const pixelPivot(screen.GtoP(m_globalPivot)); + + if (perspective) + { + if (IsBillboard()) + { + m2::RectD r = GetPixelRect(screen, false); + m2::PointD pixelPivotPerspective = screen.PtoP3d(pixelPivot); + r.Offset(-pixelPivot); + r.Offset(pixelPivotPerspective); + + return r; + } + return GetPixelRectPerspective(screen); + } + m2::RectD result; - for (gpu::TextDynamicVertex const & v : m_normals) + for (gpu::TextDynamicVertex const & v : m_buffer) result.Add(pixelPivot + glsl::ToPoint(v.m_normal)); return result; } - void GetPixelShape(ScreenBase const & screen, Rects & rects) const override + void GetPixelShape(ScreenBase const & screen, Rects & rects, bool perspective) const override { - m2::PointD const pixelPivot(screen.GtoP(m_centerPointIter.m_pos)); - for (size_t quadIndex = 0; quadIndex < m_normals.size(); quadIndex += 4) + m2::PointD const pixelPivot(screen.GtoP(m_globalPivot)); + for (size_t quadIndex = 0; quadIndex < m_buffer.size(); quadIndex += 4) { m2::RectF r; - r.Add(pixelPivot + glsl::ToPoint(m_normals[quadIndex].m_normal)); - r.Add(pixelPivot + glsl::ToPoint(m_normals[quadIndex + 1].m_normal)); - r.Add(pixelPivot + glsl::ToPoint(m_normals[quadIndex + 2].m_normal)); - r.Add(pixelPivot + glsl::ToPoint(m_normals[quadIndex + 3].m_normal)); - if (screen.PixelRect().IsIntersect(m2::RectD(r))) - rects.push_back(r); + r.Add(pixelPivot + glsl::ToPoint(m_buffer[quadIndex].m_normal)); + r.Add(pixelPivot + glsl::ToPoint(m_buffer[quadIndex + 1].m_normal)); + r.Add(pixelPivot + glsl::ToPoint(m_buffer[quadIndex + 2].m_normal)); + r.Add(pixelPivot + glsl::ToPoint(m_buffer[quadIndex + 3].m_normal)); + + if (perspective) + { + if (IsBillboard()) + { + m2::PointD const pxPivotPerspective = screen.PtoP3d(pixelPivot); + + r.Offset(-pixelPivot); + r.Offset(pxPivotPerspective); + } + else + r = m2::RectF(GetPerspectiveRect(m2::RectD(r), screen)); + } + + m2::RectD const screenRect = perspective ? screen.PixelRectIn3d() : screen.PixelRect(); + if (screenRect.IsIntersect(m2::RectD(r))) + rects.emplace_back(move(r)); } } @@ -91,11 +169,46 @@ public: return dp::kPriorityMaskManual | dp::kPriorityMaskRank; } + bool Enable3dExtention() const override + { + // Do not extend overlays for path texts. + return false; + } + + uint64_t GetPriorityInFollowingMode() const override + { + return m_priorityFollowingMode; + } + +private: + bool CalculateOffsets(const m2::Spline & pixelSpline, uint32_t & startIndex, float & pixelOffset) const + { + if (pixelSpline.GetSize() < 2) + return false; + + vector offsets; + df::PathTextLayout::CalculatePositions(offsets, pixelSpline.GetLength(), 1.0, + m_layout->GetPixelLength()); + + if (startIndex + offsets.size() <= m_textIndex) + { + startIndex += offsets.size(); + return false; + } + + ASSERT_LESS_OR_EQUAL(startIndex, m_textIndex, ()); + pixelOffset = offsets[m_textIndex - startIndex]; + return true; + } + private: m2::SharedSpline m_spline; - m2::Spline::iterator m_centerPointIter; - df::SharedTextLayout m_layout; + uint32_t const m_textIndex; + m2::PointD m_globalPivot; + float const m_globalOffset; + float const m_depth; + uint64_t const m_priorityFollowingMode; }; } @@ -109,13 +222,15 @@ PathTextShape::PathTextShape(m2::SharedSpline const & spline, , m_params(params) {} -uint64_t PathTextShape::GetOverlayPriority() const +uint64_t PathTextShape::GetOverlayPriority(bool followingMode) const { // Overlay priority for path text shapes considers length of the text. // Greater test length has more priority, because smaller texts have more chances to be shown along the road. // [6 bytes - standard overlay priority][1 byte - reserved][1 byte - length]. static uint64_t constexpr kMask = ~static_cast(0xFF); - uint64_t priority = dp::CalculateOverlayPriority(m_params.m_minVisibleScale, m_params.m_rank, m_params.m_depth); + uint64_t priority = dp::kPriorityMaskAll; + if (!followingMode) + priority = dp::CalculateOverlayPriority(m_params.m_minVisibleScale, m_params.m_rank, m_params.m_depth); priority &= kMask; priority |= (static_cast(m_params.m_text.size())); @@ -125,12 +240,13 @@ uint64_t PathTextShape::GetOverlayPriority() const void PathTextShape::DrawPathTextPlain(ref_ptr textures, ref_ptr batcher, unique_ptr && layout, - buffer_vector const & offsets) const + vector const & offsets) const { dp::TextureManager::ColorRegion color; textures->GetColorRegion(m_params.m_textFont.m_color, color); dp::GLState state(gpu::TEXT_PROGRAM, dp::GLState::OverlayLayer); + state.SetProgram3dIndex(gpu::TEXT_BILLBOARD_PROGRAM); state.SetColorTexture(color.GetTexture()); state.SetMaskTexture(layout->GetMaskTexture()); @@ -138,25 +254,24 @@ void PathTextShape::DrawPathTextPlain(ref_ptr textures, gpu::TTextStaticVertexBuffer staticBuffer; gpu::TTextDynamicVertexBuffer dynBuffer; SharedTextLayout layoutPtr(layout.release()); - for (float offset : offsets) + for (size_t textIndex = 0; textIndex < offsets.size(); ++textIndex) { + float offset = offsets[textIndex]; staticBuffer.clear(); dynBuffer.clear(); - Spline::iterator iter = m_spline.CreateIterator(); - iter.Advance(offset); - layoutPtr->CacheStaticGeometry(glsl::vec3(glsl::ToVec2(iter.m_pos), m_params.m_depth), - color, staticBuffer); - - dynBuffer.resize(staticBuffer.size(), gpu::TextDynamicVertex(glsl::vec2(0.0, 0.0))); + layoutPtr->CacheStaticGeometry(color, staticBuffer); + dynBuffer.resize(staticBuffer.size()); dp::AttributeProvider provider(2, staticBuffer.size()); provider.InitStream(0, gpu::TextStaticVertex::GetBindingInfo(), make_ref(staticBuffer.data())); provider.InitStream(1, gpu::TextDynamicVertex::GetBindingInfo(), make_ref(dynBuffer.data())); drape_ptr handle = make_unique_dp(m_spline, layoutPtr, offset, - GetOverlayPriority(), - textures); + m_params.m_depth, textIndex, + GetOverlayPriority(false /* followingMode */), + GetOverlayPriority(true /* followingMode */), + textures, true); batcher->InsertListOfStrip(state, make_ref(&provider), move(handle), 4); } } @@ -164,7 +279,7 @@ void PathTextShape::DrawPathTextPlain(ref_ptr textures, void PathTextShape::DrawPathTextOutlined(ref_ptr textures, ref_ptr batcher, unique_ptr && layout, - buffer_vector const & offsets) const + vector const & offsets) const { dp::TextureManager::ColorRegion color; dp::TextureManager::ColorRegion outline; @@ -172,6 +287,7 @@ void PathTextShape::DrawPathTextOutlined(ref_ptr textures, textures->GetColorRegion(m_params.m_textFont.m_outlineColor, outline); dp::GLState state(gpu::TEXT_OUTLINED_PROGRAM, dp::GLState::OverlayLayer); + state.SetProgram3dIndex(gpu::TEXT_OUTLINED_BILLBOARD_PROGRAM); state.SetColorTexture(color.GetTexture()); state.SetMaskTexture(layout->GetMaskTexture()); @@ -179,25 +295,24 @@ void PathTextShape::DrawPathTextOutlined(ref_ptr textures, gpu::TTextOutlinedStaticVertexBuffer staticBuffer; gpu::TTextDynamicVertexBuffer dynBuffer; SharedTextLayout layoutPtr(layout.release()); - for (float offset : offsets) + for (size_t textIndex = 0; textIndex < offsets.size(); ++textIndex) { + float offset = offsets[textIndex]; staticBuffer.clear(); dynBuffer.clear(); - Spline::iterator iter = m_spline.CreateIterator(); - iter.Advance(offset); - layoutPtr->CacheStaticGeometry(glsl::vec3(glsl::ToVec2(iter.m_pos), m_params.m_depth), - color, outline, staticBuffer); - - dynBuffer.resize(staticBuffer.size(), gpu::TextDynamicVertex(glsl::vec2(0.0, 0.0))); + layoutPtr->CacheStaticGeometry(color, outline, staticBuffer); + dynBuffer.resize(staticBuffer.size()); dp::AttributeProvider provider(2, staticBuffer.size()); provider.InitStream(0, gpu::TextOutlinedStaticVertex::GetBindingInfo(), make_ref(staticBuffer.data())); provider.InitStream(1, gpu::TextDynamicVertex::GetBindingInfo(), make_ref(dynBuffer.data())); drape_ptr handle = make_unique_dp(m_spline, layoutPtr, offset, - GetOverlayPriority(), - textures); + m_params.m_depth, textIndex, + GetOverlayPriority(false /* followingMode */), + GetOverlayPriority(true /* followingMode */), + textures, true); batcher->InsertListOfStrip(state, make_ref(&provider), move(handle), 4); } } @@ -211,38 +326,12 @@ void PathTextShape::Draw(ref_ptr batcher, ref_ptrGetPixelLength(); - float const pathGlbLength = m_spline->GetLength(); - - // on next readable scale m_scaleGtoP will be twice - if (textLength > pathGlbLength * 2.0 * m_params.m_baseGtoPScale) + vector offsets; + PathTextLayout::CalculatePositions(offsets, m_spline->GetLength(), m_params.m_baseGtoPScale, + layout->GetPixelLength()); + if (offsets.empty()) return; - float const kPathLengthScalar = 0.75; - float const pathLength = kPathLengthScalar * m_params.m_baseGtoPScale * pathGlbLength; - - float const etalonEmpty = max(300 * df::VisualParams::Instance().GetVisualScale(), (double)textLength); - float const minPeriodSize = etalonEmpty + textLength; - float const twoTextAndEmpty = minPeriodSize + textLength; - - buffer_vector offsets; - if (pathLength < twoTextAndEmpty) - { - // if we can't place 2 text and empty part on path - // we place only one text on center of path - offsets.push_back(pathGlbLength / 2.0f); - } - else - { - double const textCount = max(floor(pathLength / minPeriodSize), 1.0); - double const glbTextLen = pathGlbLength / textCount; - for (double offset = 0.5 * glbTextLen; offset < pathGlbLength; offset += glbTextLen) - offsets.push_back(offset); - } - if (m_params.m_textFont.m_outlineColor == dp::Color::Transparent()) DrawPathTextPlain(textures, batcher, move(layout), offsets); else diff --git a/drape_frontend/path_text_shape.hpp b/drape_frontend/path_text_shape.hpp index ea7c0951c4..b9dde17f22 100644 --- a/drape_frontend/path_text_shape.hpp +++ b/drape_frontend/path_text_shape.hpp @@ -17,16 +17,16 @@ public: MapShapePriority GetPriority() const override { return MapShapePriority::LinePriority; } private: - uint64_t GetOverlayPriority() const; + uint64_t GetOverlayPriority(bool followingMode) const; void DrawPathTextPlain(ref_ptr textures, ref_ptr batcher, unique_ptr && layout, - buffer_vector const & offests) const; + vector const & offests) const; void DrawPathTextOutlined(ref_ptr textures, ref_ptr batcher, unique_ptr && layout, - buffer_vector const & offsets) const; + vector const & offsets) const; m2::SharedSpline m_spline; PathTextViewParams m_params; diff --git a/drape_frontend/poi_symbol_shape.cpp b/drape_frontend/poi_symbol_shape.cpp index fc9f36c414..6889e81241 100644 --- a/drape_frontend/poi_symbol_shape.cpp +++ b/drape_frontend/poi_symbol_shape.cpp @@ -25,7 +25,7 @@ void PoiSymbolShape::Draw(ref_ptr batcher, ref_ptr batcher, ref_ptr batcher, ref_ptr handle = make_unique_dp(m_params.m_id, dp::Center, m_pt, pixelSize, - GetOverlayPriority()); + GetOverlayPriority(), + true); + handle->SetPivotZ(m_params.m_posZ); handle->SetExtendingSize(m_params.m_extendingSize); batcher->InsertTriangleStrip(state, make_ref(&provider), move(handle)); } diff --git a/drape_frontend/read_manager.cpp b/drape_frontend/read_manager.cpp index 68885e871b..84c301fd98 100755 --- a/drape_frontend/read_manager.cpp +++ b/drape_frontend/read_manager.cpp @@ -36,6 +36,9 @@ ReadManager::ReadManager(ref_ptr commutator, MapDataProvider , m_model(model) , m_pool(make_unique_dp(ReadCount(), bind(&ReadManager::OnTaskFinished, this, _1))) , m_forceUpdate(true) + , m_need3dBuildings(false) + , m_allow3dBuildings(false) + , m_modeChanged(false) , myPool(64, ReadMWMTaskFactory(m_memIndex, m_model)) , m_counter(0) , m_generationCounter(0) @@ -70,14 +73,20 @@ void ReadManager::OnTaskFinished(threads::IRoutine * task) myPool.Return(t); } -void ReadManager::UpdateCoverage(ScreenBase const & screen, TTilesCollection const & tiles, ref_ptr texMng) +void ReadManager::UpdateCoverage(ScreenBase const & screen, bool is3dBuildings, + TTilesCollection const & tiles, ref_ptr texMng) { if (screen == m_currentViewport && !m_forceUpdate) return; m_forceUpdate = false; - if (MustDropAllTiles(screen)) + m_modeChanged |= m_need3dBuildings != is3dBuildings; + m_need3dBuildings = is3dBuildings; + + if (m_modeChanged || MustDropAllTiles(screen)) { + m_modeChanged = false; + IncreaseCounter(static_cast(tiles.size())); m_generationCounter++; @@ -113,8 +122,8 @@ void ReadManager::UpdateCoverage(ScreenBase const & screen, TTilesCollection con { if (IsNeighbours(tile->GetTileKey(), outTile->GetTileKey())) { - rereadTiles.push_back(tile); - break; + rereadTiles.push_back(tile); + break; } } } @@ -180,6 +189,7 @@ void ReadManager::PushTaskBackForTileKey(TileKey const & tileKey, ref_ptr tileInfo(new TileInfo(make_unique_dp(TileKey(tileKey, m_generationCounter), m_commutator, texMng))); + tileInfo->Set3dBuildings(m_need3dBuildings && m_allow3dBuildings); m_tileInfos.insert(tileInfo); ReadMWMTask * task = myPool.Get(); task->Init(tileInfo); @@ -188,6 +198,7 @@ void ReadManager::PushTaskBackForTileKey(TileKey const & tileKey, ref_ptr const & tileToReread) { + tileToReread->Set3dBuildings(m_need3dBuildings && m_allow3dBuildings); ReadMWMTask * task = myPool.Get(); task->Init(tileToReread); m_pool->PushFront(task); @@ -210,4 +221,14 @@ void ReadManager::IncreaseCounter(int value) m_counter += value; } +void ReadManager::Allow3dBuildings(bool allow3dBuildings) +{ + if (m_allow3dBuildings != allow3dBuildings) + { + m_modeChanged = true; + m_forceUpdate = true; + m_allow3dBuildings = allow3dBuildings; + } +} + } // namespace df diff --git a/drape_frontend/read_manager.hpp b/drape_frontend/read_manager.hpp index bb61ac02cc..34fba548cf 100755 --- a/drape_frontend/read_manager.hpp +++ b/drape_frontend/read_manager.hpp @@ -30,11 +30,13 @@ class ReadManager public: ReadManager(ref_ptr commutator, MapDataProvider & model); - void UpdateCoverage(ScreenBase const & screen, TTilesCollection const & tiles, ref_ptr texMng); + void UpdateCoverage(ScreenBase const & screen, bool is3dBuildings, TTilesCollection const & tiles, + ref_ptr texMng); void Invalidate(TTilesCollection const & keyStorage); void Stop(); bool CheckTileKey(TileKey const & tileKey) const; + void Allow3dBuildings(bool allow3dBuildings); static size_t ReadCount(); @@ -55,6 +57,9 @@ private: ScreenBase m_currentViewport; bool m_forceUpdate; + bool m_need3dBuildings; + bool m_allow3dBuildings; + bool m_modeChanged; struct LessByTileInfo { diff --git a/drape_frontend/render_group.cpp b/drape_frontend/render_group.cpp index 820c7f1e3c..0ee5139a80 100755 --- a/drape_frontend/render_group.cpp +++ b/drape_frontend/render_group.cpp @@ -3,6 +3,7 @@ #include "drape/debug_rect_renderer.hpp" #include "drape/shader_def.hpp" +#include "drape/vertex_array_buffer.hpp" #include "geometry/screenbase.hpp" @@ -13,9 +14,11 @@ namespace df { -void BaseRenderGroup::SetRenderParams(ref_ptr shader, ref_ptr generalUniforms) +void BaseRenderGroup::SetRenderParams(ref_ptr shader, ref_ptr shader3d, + ref_ptr generalUniforms) { m_shader = shader; + m_shader3d = shader3d; m_generalUniforms = generalUniforms; } @@ -24,14 +27,15 @@ void BaseRenderGroup::UpdateAnimation() m_uniforms.SetFloatValue("u_opacity", 1.0); } -void BaseRenderGroup::Render(const ScreenBase &) +void BaseRenderGroup::Render(const ScreenBase & screen) { - ASSERT(m_shader != nullptr, ()); + ref_ptr shader = screen.isPerspective() ? m_shader3d : m_shader; + ASSERT(shader != nullptr, ()); ASSERT(m_generalUniforms != nullptr, ()); - m_shader->Bind(); - dp::ApplyState(m_state, m_shader); - dp::ApplyUniforms(*(m_generalUniforms.get()), m_shader); + shader->Bind(); + dp::ApplyState(m_state, shader); + dp::ApplyUniforms(*(m_generalUniforms.get()), shader); } bool BaseRenderGroup::IsOverlay() const @@ -73,33 +77,40 @@ void RenderGroup::Render(ScreenBase const & screen) { BaseRenderGroup::Render(screen); + ref_ptr shader = screen.isPerspective() ? m_shader3d : m_shader; + for(auto & renderBucket : m_renderBuckets) + renderBucket->GetBuffer()->Build(shader); + auto const & params = df::VisualParams::Instance().GetGlyphVisualParams(); int programIndex = m_state.GetProgramIndex(); - if (programIndex == gpu::TEXT_OUTLINED_PROGRAM) + int program3dIndex = m_state.GetProgram3dIndex(); + if (programIndex == gpu::TEXT_OUTLINED_PROGRAM || + program3dIndex == gpu::TEXT_OUTLINED_BILLBOARD_PROGRAM) { m_uniforms.SetFloatValue("u_contrastGamma", params.m_outlineContrast, params.m_outlineGamma); m_uniforms.SetFloatValue("u_isOutlinePass", 1.0f); - dp::ApplyUniforms(m_uniforms, m_shader); + dp::ApplyUniforms(m_uniforms, shader); for(auto & renderBucket : m_renderBuckets) renderBucket->Render(screen); m_uniforms.SetFloatValue("u_contrastGamma", params.m_contrast, params.m_gamma); m_uniforms.SetFloatValue("u_isOutlinePass", 0.0f); - dp::ApplyUniforms(m_uniforms, m_shader); + dp::ApplyUniforms(m_uniforms, shader); for(auto & renderBucket : m_renderBuckets) renderBucket->Render(screen); } - else if(programIndex == gpu::TEXT_PROGRAM) + else if (programIndex == gpu::TEXT_PROGRAM || + program3dIndex == gpu::TEXT_BILLBOARD_PROGRAM) { m_uniforms.SetFloatValue("u_contrastGamma", params.m_contrast, params.m_gamma); - dp::ApplyUniforms(m_uniforms, m_shader); + dp::ApplyUniforms(m_uniforms, shader); for(auto & renderBucket : m_renderBuckets) renderBucket->Render(screen); } else { - dp::ApplyUniforms(m_uniforms, m_shader); + dp::ApplyUniforms(m_uniforms, shader); for(drape_ptr & renderBucket : m_renderBuckets) renderBucket->Render(screen); @@ -170,7 +181,8 @@ void RenderGroup::Disappear() //else { // Create separate disappearing animation for area objects to eliminate flickering. - if (m_state.GetProgramIndex() == gpu::AREA_PROGRAM) + if (m_state.GetProgramIndex() == gpu::AREA_PROGRAM || + m_state.GetProgramIndex() == gpu::AREA_3D_PROGRAM) m_disappearAnimation = make_unique(0.01 /* duration */, 0.25 /* delay */, 1.0 /* startOpacity */, 1.0 /* endOpacity */); } @@ -238,9 +250,13 @@ void UserMarkRenderGroup::UpdateAnimation() void UserMarkRenderGroup::Render(ScreenBase const & screen) { BaseRenderGroup::Render(screen); - dp::ApplyUniforms(m_uniforms, m_shader); + ref_ptr shader = screen.isPerspective() ? m_shader3d : m_shader; + dp::ApplyUniforms(m_uniforms, shader); if (m_renderBucket != nullptr) + { + m_renderBucket->GetBuffer()->Build(shader); m_renderBucket->Render(screen); + } } string DebugPrint(RenderGroup const & group) diff --git a/drape_frontend/render_group.hpp b/drape_frontend/render_group.hpp index 8d8250cd33..978b35321f 100755 --- a/drape_frontend/render_group.hpp +++ b/drape_frontend/render_group.hpp @@ -25,7 +25,8 @@ public: : m_state(state) , m_tileKey(tileKey) {} - void SetRenderParams(ref_ptr shader, ref_ptr generalUniforms); + void SetRenderParams(ref_ptr shader, ref_ptr shader3d, + ref_ptr generalUniforms); dp::GLState const & GetState() const { return m_state; } TileKey const & GetTileKey() const { return m_tileKey; } @@ -33,11 +34,12 @@ public: bool IsOverlay() const; virtual void UpdateAnimation(); - virtual void Render(ScreenBase const & /*screen*/); + virtual void Render(ScreenBase const & screen); protected: dp::GLState m_state; ref_ptr m_shader; + ref_ptr m_shader3d; dp::UniformValuesStorage m_uniforms; ref_ptr m_generalUniforms; diff --git a/drape_frontend/requested_tiles.cpp b/drape_frontend/requested_tiles.cpp index 76d0cf4bfe..fab0e0dc1e 100755 --- a/drape_frontend/requested_tiles.cpp +++ b/drape_frontend/requested_tiles.cpp @@ -3,11 +3,12 @@ namespace df { -void RequestedTiles::Set(ScreenBase const & screen, TTilesCollection && tiles) +void RequestedTiles::Set(ScreenBase const & screen, bool is3dBuildings, TTilesCollection && tiles) { lock_guard lock(m_mutex); m_tiles = move(tiles); m_screen = screen; + m_is3dBuildings = is3dBuildings; } TTilesCollection RequestedTiles::GetTiles() @@ -26,6 +27,12 @@ ScreenBase RequestedTiles::GetScreen() return m_screen; } +bool RequestedTiles::Is3dBuildings() +{ + lock_guard lock(m_mutex); + return m_is3dBuildings; +} + bool RequestedTiles::CheckTileKey(TileKey const & tileKey) const { lock_guard lock(m_mutex); diff --git a/drape_frontend/requested_tiles.hpp b/drape_frontend/requested_tiles.hpp index c9bbd22d69..24fc6b3689 100755 --- a/drape_frontend/requested_tiles.hpp +++ b/drape_frontend/requested_tiles.hpp @@ -13,14 +13,16 @@ class RequestedTiles { public: RequestedTiles() = default; - void Set(ScreenBase const & screen, TTilesCollection && tiles); + void Set(ScreenBase const & screen, bool is3dBuildings, TTilesCollection && tiles); TTilesCollection GetTiles(); ScreenBase GetScreen(); + bool Is3dBuildings(); bool CheckTileKey(TileKey const & tileKey) const; private: TTilesCollection m_tiles; ScreenBase m_screen; + bool m_is3dBuildings = false; mutable mutex m_mutex; }; diff --git a/drape_frontend/route_renderer.cpp b/drape_frontend/route_renderer.cpp index 863574e6f1..883f5d20c7 100644 --- a/drape_frontend/route_renderer.cpp +++ b/drape_frontend/route_renderer.cpp @@ -241,13 +241,18 @@ void RouteRenderer::RenderRouteSign(drape_ptr const & sign, Scree dp::UniformValuesStorage uniforms = commonUniforms; uniforms.SetFloatValue("u_opacity", 1.0f); - ref_ptr program = mng->GetProgram(state.GetProgramIndex()); + ref_ptr program = screen.isPerspective() ? mng->GetProgram(state.GetProgram3dIndex()) + : mng->GetProgram(state.GetProgramIndex()); program->Bind(); + dp::ApplyState(sign->m_sign.m_state, program); dp::ApplyUniforms(uniforms, program); for (auto const & bucket : sign->m_sign.m_buckets) + { + bucket->GetBuffer()->Build(program); bucket->Render(screen); + } } void RouteRenderer::RenderArrow(ref_ptr prg, drape_ptr const & property, diff --git a/drape_frontend/route_shape.cpp b/drape_frontend/route_shape.cpp index fa6659a4c1..dae2da1952 100644 --- a/drape_frontend/route_shape.cpp +++ b/drape_frontend/route_shape.cpp @@ -289,7 +289,7 @@ void RouteShape::CacheRouteSign(ref_ptr mng, RouteSignData & m2::PointF halfSize = m2::PointF(symbol.GetPixelSize()) * 0.5f; glsl::vec2 const pos = glsl::ToVec2(routeSignData.m_position); - glsl::vec3 const pivot = glsl::vec3(pos.x, pos.y, m_params.m_depth); + glsl::vec4 const pivot = glsl::vec4(pos.x, pos.y, m_params.m_depth, 0.0f); gpu::SolidTexturingVertex data[4]= { { pivot, glsl::vec2(-halfSize.x, halfSize.y), glsl::ToVec2(texRect.LeftTop()) }, @@ -299,6 +299,8 @@ void RouteShape::CacheRouteSign(ref_ptr mng, RouteSignData & }; dp::GLState state(gpu::TEXTURING_PROGRAM, dp::GLState::OverlayLayer); + if (!routeSignData.m_isStart) + state.SetProgram3dIndex(gpu::TEXTURING_BILLBOARD_PROGRAM); state.SetColorTexture(symbol.GetTexture()); { diff --git a/drape_frontend/rule_drawer.cpp b/drape_frontend/rule_drawer.cpp index 316dd17e02..338bf777cf 100644 --- a/drape_frontend/rule_drawer.cpp +++ b/drape_frontend/rule_drawer.cpp @@ -8,6 +8,7 @@ #include "indexer/feature_algo.hpp" #include "indexer/feature_visibility.hpp" #include "indexer/scales.hpp" +#include "indexer/ftypes_matcher.hpp" #include "base/assert.hpp" #include "std/bind.hpp" @@ -37,11 +38,12 @@ size_t kMinFlushSizes[df::PrioritiesCount] = RuleDrawer::RuleDrawer(TDrawerCallback const & fn, TCheckCancelledCallback const & checkCancelled, TIsCountryLoadedByNameFn const & isLoadedFn, - ref_ptr context) + ref_ptr context, bool is3dBuildings) : m_callback(fn) , m_checkCancelled(checkCancelled) , m_isLoadedFn(isLoadedFn) , m_context(context) + , m_is3dBuidings(is3dBuildings) , m_wasCancelled(false) { ASSERT(m_callback != nullptr, ()); @@ -132,7 +134,45 @@ void RuleDrawer::operator()(FeatureType const & f) if (s.AreaStyleExists()) { - ApplyAreaFeature apply(insertShape, f.GetID(), minVisibleScale, f.GetRank(), s.GetCaptionDescription()); + bool is3dBuilding = false; + if (m_is3dBuidings && f.GetLayer() >= 0) + { + is3dBuilding = (ftypes::IsBuildingChecker::Instance()(f) || ftypes::IsBuildingPartChecker::Instance()(f)) && + !ftypes::IsBridgeChecker::Instance()(f) && + !ftypes::IsTunnelChecker::Instance()(f); + } + + float areaHeight = 0.0f; + float areaMinHeight = 0.0f; + if (is3dBuilding) + { + f.ParseMetadata(); + feature::Metadata const & md = f.GetMetadata(); + string value = md.Get(feature::Metadata::FMD_HEIGHT); + + double const kDefaultHeightInMeters = 3.0; + double heightInMeters = kDefaultHeightInMeters; + if (!value.empty()) + strings::to_double(value, heightInMeters); + + value = md.Get(feature::Metadata::FMD_MIN_HEIGHT); + double minHeigthInMeters = 0.0; + if (!value.empty()) + strings::to_double(value, minHeigthInMeters); + + m2::PointD const pt = feature::GetCenter(f, zoomLevel); + double const lon = MercatorBounds::XToLon(pt.x); + double const lat = MercatorBounds::YToLat(pt.y); + + m2::RectD rectMercator = MercatorBounds::MetresToXY(lon, lat, heightInMeters); + areaHeight = m2::PointD(rectMercator.SizeX(), rectMercator.SizeY()).Length(); + + rectMercator = MercatorBounds::MetresToXY(lon, lat, minHeigthInMeters); + areaMinHeight = m2::PointD(rectMercator.SizeX(), rectMercator.SizeY()).Length(); + } + + ApplyAreaFeature apply(insertShape, f.GetID(), areaMinHeight, areaHeight, + minVisibleScale, f.GetRank(), s.GetCaptionDescription()); f.ForEachTriangleRef(apply, zoomLevel); if (s.PointStyleExists()) @@ -162,7 +202,7 @@ void RuleDrawer::operator()(FeatureType const & f) else { ASSERT(s.PointStyleExists(), ()); - ApplyPointFeature apply(insertShape, f.GetID(), minVisibleScale, f.GetRank(), s.GetCaptionDescription()); + ApplyPointFeature apply(insertShape, f.GetID(), minVisibleScale, f.GetRank(), s.GetCaptionDescription(), 0.0f /* posZ */); f.ForEachPointRef(apply, zoomLevel); if (CheckCancelled()) diff --git a/drape_frontend/rule_drawer.hpp b/drape_frontend/rule_drawer.hpp index 2b89e47388..41e35dd0c0 100644 --- a/drape_frontend/rule_drawer.hpp +++ b/drape_frontend/rule_drawer.hpp @@ -29,7 +29,7 @@ public: using TIsCountryLoadedByNameFn = function; RuleDrawer(TDrawerCallback const & drawerFn, TCheckCancelledCallback const & checkCancelled, - TIsCountryLoadedByNameFn const & isLoadedFn, ref_ptr context); + TIsCountryLoadedByNameFn const & isLoadedFn, ref_ptr context, bool is3dBuildings); ~RuleDrawer(); void operator() (FeatureType const & f); @@ -45,6 +45,8 @@ private: m2::RectD m_globalRect; double m_currentScaleGtoP; + bool const m_is3dBuidings; + array m_mapShapes; bool m_wasCancelled; }; diff --git a/drape_frontend/selection_shape.cpp b/drape_frontend/selection_shape.cpp index b87aa5e7a3..ec2fde739d 100644 --- a/drape_frontend/selection_shape.cpp +++ b/drape_frontend/selection_shape.cpp @@ -54,6 +54,7 @@ dp::BindingInfo GetBindingInfo() SelectionShape::SelectionShape(ref_ptr mng) : m_position(m2::PointD::Zero()) + , m_positionZ(0.0) , m_animation(false, 0.25) , m_selectedObject(OBJECT_EMPTY) { @@ -103,10 +104,11 @@ SelectionShape::SelectionShape(ref_ptr mng) m_mapping.AddRangePoint(1.0, r); } -void SelectionShape::Show(ESelectedObject obj, m2::PointD const & position, bool isAnimate) +void SelectionShape::Show(ESelectedObject obj, m2::PointD const & position, double positionZ, bool isAnimate) { m_animation.Hide(); m_position = position; + m_positionZ = positionZ; m_selectedObject = obj; if (isAnimate) m_animation.ShowAnimated(); @@ -123,14 +125,22 @@ void SelectionShape::Hide() void SelectionShape::Render(ScreenBase const & screen, ref_ptr mng, dp::UniformValuesStorage const & commonUniforms) { - UNUSED_VALUE(screen); ShowHideAnimation::EState state = m_animation.GetState(); if (state == ShowHideAnimation::STATE_VISIBLE || state == ShowHideAnimation::STATE_SHOW_DIRECTION) { dp::UniformValuesStorage uniforms = commonUniforms; - uniforms.SetFloatValue("u_position", m_position.x, m_position.y, 0.0); - uniforms.SetFloatValue("u_accuracy", m_mapping.GetValue(m_animation.GetT())); + uniforms.SetFloatValue("u_position", m_position.x, m_position.y, -m_positionZ); + + float accuracy = m_mapping.GetValue(m_animation.GetT()); + if (screen.isPerspective()) + { + m2::PointD const pt1 = screen.GtoP(m_position); + m2::PointD const pt2(pt1.x + 1, pt1.y); + float const scale = screen.PtoP3d(pt2).x - screen.PtoP3d(pt1).x; + accuracy /= scale; + } + uniforms.SetFloatValue("u_accuracy", accuracy); uniforms.SetFloatValue("u_opacity", 1.0f); m_renderNode->Render(mng, uniforms); } diff --git a/drape_frontend/selection_shape.hpp b/drape_frontend/selection_shape.hpp index 64177cf998..8eab71f90e 100644 --- a/drape_frontend/selection_shape.hpp +++ b/drape_frontend/selection_shape.hpp @@ -33,7 +33,7 @@ public: SelectionShape(ref_ptr mng); void SetPosition(m2::PointD const & position) { m_position = position; } - void Show(ESelectedObject obj, m2::PointD const & position, bool isAnimate); + void Show(ESelectedObject obj, m2::PointD const & position, double positionZ, bool isAnimate); void Hide(); void Render(ScreenBase const & screen, ref_ptr mng, @@ -46,6 +46,7 @@ private: private: m2::PointD m_position; + double m_positionZ; ShowHideAnimation m_animation; ESelectedObject m_selectedObject; diff --git a/drape_frontend/shape_view_params.hpp b/drape_frontend/shape_view_params.hpp index 0022cc6e3d..6a7e857d6a 100644 --- a/drape_frontend/shape_view_params.hpp +++ b/drape_frontend/shape_view_params.hpp @@ -26,6 +26,7 @@ struct PoiSymbolViewParams : CommonViewParams FeatureID m_id; string m_symbolName; uint32_t m_extendingSize; + float m_posZ; }; struct CircleViewParams : CommonViewParams @@ -40,6 +41,8 @@ struct CircleViewParams : CommonViewParams struct AreaViewParams : CommonViewParams { dp::Color m_color; + float m_minPosZ; + float m_posZ; }; struct LineViewParams : CommonViewParams @@ -66,6 +69,7 @@ struct TextViewParams : CommonViewParams bool m_primaryOptional; bool m_secondaryOptional; uint32_t m_extendingSize; + float m_posZ; }; struct PathTextViewParams : CommonViewParams diff --git a/drape_frontend/stylist.cpp b/drape_frontend/stylist.cpp index c89c3ff7c1..cf9d509a0d 100644 --- a/drape_frontend/stylist.cpp +++ b/drape_frontend/stylist.cpp @@ -333,8 +333,12 @@ CaptionDescription & Stylist::GetCaptionDescriptionImpl() return m_captionDescriptor; } -bool InitStylist(FeatureType const & f, int const zoomLevel, Stylist & s) +bool InitStylist(FeatureType const & f, int const zoomLevel, bool buildings3d, Stylist & s) { + if (!buildings3d && ftypes::IsBuildingPartChecker::Instance()(f) && + !ftypes::IsBuildingChecker::Instance()(f)) + return false; + drule::KeysT keys; pair geomType = feature::GetDrawRule(f, zoomLevel, keys); diff --git a/drape_frontend/stylist.hpp b/drape_frontend/stylist.hpp index 3cbb4fc047..fd2b7df4aa 100644 --- a/drape_frontend/stylist.hpp +++ b/drape_frontend/stylist.hpp @@ -63,6 +63,7 @@ public: private: friend bool InitStylist(FeatureType const &, int const, + bool buildings3d, Stylist &); void RaiseCoastlineFlag(); @@ -82,6 +83,7 @@ private: bool InitStylist(FeatureType const & f, int const zoomLevel, + bool buildings3d, Stylist & s); } // namespace df diff --git a/drape_frontend/text_handle.cpp b/drape_frontend/text_handle.cpp index 8bd55a03b7..c38c9322ba 100644 --- a/drape_frontend/text_handle.cpp +++ b/drape_frontend/text_handle.cpp @@ -7,8 +7,9 @@ namespace df TextHandle::TextHandle(FeatureID const & id, strings::UniString const & text, dp::Anchor anchor, uint64_t priority, - ref_ptr textureManager) - : OverlayHandle(id, anchor, priority) + ref_ptr textureManager, + bool isBillboard) + : OverlayHandle(id, anchor, priority, isBillboard) , m_forceUpdateNormals(false) , m_isLastVisible(false) , m_text(text) @@ -19,9 +20,10 @@ TextHandle::TextHandle(FeatureID const & id, strings::UniString const & text, TextHandle::TextHandle(FeatureID const & id, strings::UniString const & text, dp::Anchor anchor, uint64_t priority, ref_ptr textureManager, - gpu::TTextDynamicVertexBuffer && normals) - : OverlayHandle(id, anchor, priority) - , m_normals(move(normals)) + gpu::TTextDynamicVertexBuffer && normals, + bool isBillboard) + : OverlayHandle(id, anchor, priority, isBillboard) + , m_buffer(move(normals)) , m_forceUpdateNormals(false) , m_isLastVisible(false) , m_text(text) @@ -40,12 +42,12 @@ void TextHandle::GetAttributeMutation(ref_ptr mutato TOffsetNode const & node = GetOffsetNode(gpu::TextDynamicVertex::GetDynamicStreamID()); ASSERT(node.first.GetElementSize() == sizeof(gpu::TextDynamicVertex), ()); - ASSERT(node.second.m_count == m_normals.size(), ()); + ASSERT(node.second.m_count == m_buffer.size(), ()); - uint32_t byteCount = m_normals.size() * sizeof(gpu::TextDynamicVertex); + uint32_t byteCount = m_buffer.size() * sizeof(gpu::TextDynamicVertex); void * buffer = mutator->AllocateMutationBuffer(byteCount); if (isVisible) - memcpy(buffer, m_normals.data(), byteCount); + memcpy(buffer, m_buffer.data(), byteCount); else memset(buffer, 0, byteCount); diff --git a/drape_frontend/text_handle.hpp b/drape_frontend/text_handle.hpp index 94e303f4ef..7524f7eb95 100644 --- a/drape_frontend/text_handle.hpp +++ b/drape_frontend/text_handle.hpp @@ -19,12 +19,14 @@ class TextHandle : public dp::OverlayHandle public: TextHandle(FeatureID const & id, strings::UniString const & text, dp::Anchor anchor, uint64_t priority, - ref_ptr textureManager); + ref_ptr textureManager, + bool isBillboard = false); TextHandle(FeatureID const & id, strings::UniString const & text, dp::Anchor anchor, uint64_t priority, ref_ptr textureManager, - gpu::TTextDynamicVertexBuffer && normals); + gpu::TTextDynamicVertexBuffer && normals, + bool IsBillboard = false); bool Update(ScreenBase const & screen) override; @@ -36,7 +38,7 @@ public: void SetForceUpdateNormals(bool forceUpdate) const; protected: - gpu::TTextDynamicVertexBuffer m_normals; + gpu::TTextDynamicVertexBuffer m_buffer; mutable bool m_forceUpdateNormals; private: diff --git a/drape_frontend/text_layout.cpp b/drape_frontend/text_layout.cpp index 7743a6e750..932509ddc3 100644 --- a/drape_frontend/text_layout.cpp +++ b/drape_frontend/text_layout.cpp @@ -1,4 +1,5 @@ #include "drape_frontend/text_layout.hpp" +#include "drape_frontend/visual_params.hpp" #include "drape_frontend/visual_params.hpp" @@ -21,11 +22,9 @@ float const kValidSplineTurn = 0.96f; class TextGeometryGenerator { public: - TextGeometryGenerator(glsl::vec3 const & pivot, - dp::TextureManager::ColorRegion const & color, + TextGeometryGenerator(dp::TextureManager::ColorRegion const & color, gpu::TTextStaticVertexBuffer & buffer) - : m_pivot(pivot) - , m_colorCoord(glsl::ToVec2(color.GetTexRect().Center())) + : m_colorCoord(glsl::ToVec2(color.GetTexRect().Center())) , m_buffer(buffer) { } @@ -33,14 +32,14 @@ public: void operator() (dp::TextureManager::GlyphRegion const & glyph) { m2::RectF const & mask = glyph.GetTexRect(); - m_buffer.push_back(gpu::TextStaticVertex(m_pivot, m_colorCoord, glsl::ToVec2(mask.LeftTop()))); - m_buffer.push_back(gpu::TextStaticVertex(m_pivot, m_colorCoord, glsl::ToVec2(mask.LeftBottom()))); - m_buffer.push_back(gpu::TextStaticVertex(m_pivot, m_colorCoord, glsl::ToVec2(mask.RightTop()))); - m_buffer.push_back(gpu::TextStaticVertex(m_pivot, m_colorCoord, glsl::ToVec2(mask.RightBottom()))); + + m_buffer.emplace_back(gpu::TextStaticVertex(m_colorCoord, glsl::ToVec2(mask.LeftTop()))); + m_buffer.emplace_back(gpu::TextStaticVertex(m_colorCoord, glsl::ToVec2(mask.LeftBottom()))); + m_buffer.emplace_back(gpu::TextStaticVertex(m_colorCoord, glsl::ToVec2(mask.RightTop()))); + m_buffer.emplace_back(gpu::TextStaticVertex(m_colorCoord, glsl::ToVec2(mask.RightBottom()))); } protected: - glsl::vec3 const & m_pivot; glsl::vec2 m_colorCoord; gpu::TTextStaticVertexBuffer & m_buffer; }; @@ -49,12 +48,13 @@ class StraigthTextGeometryGenerator : public TextGeometryGenerator { typedef TextGeometryGenerator TBase; public: - StraigthTextGeometryGenerator(glsl::vec3 const & pivot, glsl::vec2 const & pixelOffset, + StraigthTextGeometryGenerator(glsl::vec4 const & pivot, glsl::vec2 const & pixelOffset, float textRatio, dp::TextureManager::ColorRegion const & color, gpu::TTextStaticVertexBuffer & staticBuffer, gpu::TTextDynamicVertexBuffer & dynBuffer) - : TBase(pivot, color, staticBuffer) + : TBase(color, staticBuffer) + , m_pivot(pivot) , m_penPosition(pixelOffset) , m_buffer(dynBuffer) , m_textRatio(textRatio) @@ -79,16 +79,17 @@ public: m_penPosition += glsl::vec2(-xOffset, 0.0f); } - m_buffer.push_back(gpu::TextDynamicVertex(m_penPosition + glsl::vec2(xOffset, bottomVector))); - m_buffer.push_back(gpu::TextDynamicVertex(m_penPosition + glsl::vec2(xOffset, upVector))); - m_buffer.push_back(gpu::TextDynamicVertex(m_penPosition + glsl::vec2(pixelSize.x + xOffset, bottomVector))); - m_buffer.push_back(gpu::TextDynamicVertex(m_penPosition + glsl::vec2(pixelSize.x + xOffset, upVector))); + m_buffer.emplace_back(gpu::TextDynamicVertex(m_pivot, m_penPosition + glsl::vec2(xOffset, bottomVector))); + m_buffer.emplace_back(gpu::TextDynamicVertex(m_pivot, m_penPosition + glsl::vec2(xOffset, upVector))); + m_buffer.emplace_back(gpu::TextDynamicVertex(m_pivot, m_penPosition + glsl::vec2(pixelSize.x + xOffset, bottomVector))); + m_buffer.emplace_back(gpu::TextDynamicVertex(m_pivot, m_penPosition + glsl::vec2(pixelSize.x + xOffset, upVector))); m_penPosition += glsl::vec2(glyph.GetAdvanceX() * m_textRatio, glyph.GetAdvanceY() * m_textRatio); TBase::operator()(glyph); } private: + glsl::vec4 const & m_pivot; glsl::vec2 m_penPosition; gpu::TTextDynamicVertexBuffer & m_buffer; float m_textRatio = 0.0f; @@ -98,12 +99,10 @@ private: class TextOutlinedGeometryGenerator { public: - TextOutlinedGeometryGenerator(glsl::vec3 const & pivot, - dp::TextureManager::ColorRegion const & color, - dp::TextureManager::ColorRegion const & outline, - gpu::TTextOutlinedStaticVertexBuffer & buffer) - : m_pivot(pivot) - , m_colorCoord(glsl::ToVec2(color.GetTexRect().Center())) + TextOutlinedGeometryGenerator(dp::TextureManager::ColorRegion const & color, + dp::TextureManager::ColorRegion const & outline, + gpu::TTextOutlinedStaticVertexBuffer & buffer) + : m_colorCoord(glsl::ToVec2(color.GetTexRect().Center())) , m_outlineCoord(glsl::ToVec2(outline.GetTexRect().Center())) , m_buffer(buffer) { @@ -112,14 +111,13 @@ public: void operator() (dp::TextureManager::GlyphRegion const & glyph) { m2::RectF const & mask = glyph.GetTexRect(); - m_buffer.push_back(gpu::TextOutlinedStaticVertex(m_pivot, m_colorCoord, m_outlineCoord, glsl::ToVec2(mask.LeftTop()))); - m_buffer.push_back(gpu::TextOutlinedStaticVertex(m_pivot, m_colorCoord, m_outlineCoord, glsl::ToVec2(mask.LeftBottom()))); - m_buffer.push_back(gpu::TextOutlinedStaticVertex(m_pivot, m_colorCoord, m_outlineCoord, glsl::ToVec2(mask.RightTop()))); - m_buffer.push_back(gpu::TextOutlinedStaticVertex(m_pivot, m_colorCoord, m_outlineCoord, glsl::ToVec2(mask.RightBottom()))); + m_buffer.emplace_back(gpu::TextOutlinedStaticVertex(m_colorCoord, m_outlineCoord, glsl::ToVec2(mask.LeftTop()))); + m_buffer.emplace_back(gpu::TextOutlinedStaticVertex(m_colorCoord, m_outlineCoord, glsl::ToVec2(mask.LeftBottom()))); + m_buffer.emplace_back(gpu::TextOutlinedStaticVertex(m_colorCoord, m_outlineCoord, glsl::ToVec2(mask.RightTop()))); + m_buffer.emplace_back(gpu::TextOutlinedStaticVertex(m_colorCoord, m_outlineCoord, glsl::ToVec2(mask.RightBottom()))); } protected: - glsl::vec3 const & m_pivot; glsl::vec2 m_colorCoord; glsl::vec2 m_outlineCoord; gpu::TTextOutlinedStaticVertexBuffer & m_buffer; @@ -129,13 +127,14 @@ class StraigthTextOutlinedGeometryGenerator : public TextOutlinedGeometryGenerat { typedef TextOutlinedGeometryGenerator TBase; public: - StraigthTextOutlinedGeometryGenerator(glsl::vec3 const & pivot, glsl::vec2 const & pixelOffset, + StraigthTextOutlinedGeometryGenerator(glsl::vec4 const & pivot, glsl::vec2 const & pixelOffset, float textRatio, dp::TextureManager::ColorRegion const & color, dp::TextureManager::ColorRegion const & outline, gpu::TTextOutlinedStaticVertexBuffer & staticBuffer, gpu::TTextDynamicVertexBuffer & dynBuffer) - : TBase(pivot, color, outline, staticBuffer) + : TBase(color, outline, staticBuffer) + , m_pivot(pivot) , m_penPosition(pixelOffset) , m_buffer(dynBuffer) , m_textRatio(textRatio) @@ -160,16 +159,17 @@ public: m_penPosition += glsl::vec2(-xOffset, 0.0f); } - m_buffer.push_back(gpu::TextDynamicVertex(m_penPosition + glsl::vec2(xOffset, bottomVector))); - m_buffer.push_back(gpu::TextDynamicVertex(m_penPosition + glsl::vec2(xOffset, upVector))); - m_buffer.push_back(gpu::TextDynamicVertex(m_penPosition + glsl::vec2(pixelSize.x + xOffset, bottomVector))); - m_buffer.push_back(gpu::TextDynamicVertex(m_penPosition + glsl::vec2(pixelSize.x + xOffset, upVector))); + m_buffer.emplace_back(gpu::TextDynamicVertex(m_pivot, m_penPosition + glsl::vec2(xOffset, bottomVector))); + m_buffer.emplace_back(gpu::TextDynamicVertex(m_pivot, m_penPosition + glsl::vec2(xOffset, upVector))); + m_buffer.emplace_back(gpu::TextDynamicVertex(m_pivot, m_penPosition + glsl::vec2(pixelSize.x + xOffset, bottomVector))); + m_buffer.emplace_back(gpu::TextDynamicVertex(m_pivot, m_penPosition + glsl::vec2(pixelSize.x + xOffset, upVector))); m_penPosition += glsl::vec2(glyph.GetAdvanceX() * m_textRatio, glyph.GetAdvanceY() * m_textRatio); TBase::operator()(glyph); } private: + glsl::vec4 const & m_pivot; glsl::vec2 m_penPosition; gpu::TTextDynamicVertexBuffer & m_buffer; float m_textRatio = 0.0f; @@ -399,7 +399,7 @@ StraightTextLayout::StraightTextLayout(strings::UniString const & text, float fo CalculateOffsets(anchor, m_textSizeRatio, m_metrics, delimIndexes, m_offsets, m_pixelSize); } -void StraightTextLayout::Cache(glm::vec3 const & pivot, glm::vec2 const & pixelOffset, +void StraightTextLayout::Cache(glm::vec4 const & pivot, glm::vec2 const & pixelOffset, dp::TextureManager::ColorRegion const & colorRegion, dp::TextureManager::ColorRegion const & outlineRegion, gpu::TTextOutlinedStaticVertexBuffer & staticBuffer, @@ -418,7 +418,7 @@ void StraightTextLayout::Cache(glm::vec3 const & pivot, glm::vec2 const & pixelO } } -void StraightTextLayout::Cache(glm::vec3 const & pivot, glm::vec2 const & pixelOffset, +void StraightTextLayout::Cache(glm::vec4 const & pivot, glm::vec2 const & pixelOffset, dp::TextureManager::ColorRegion const & color, gpu::TTextStaticVertexBuffer & staticBuffer, gpu::TTextDynamicVertexBuffer & dynamicBuffer) const @@ -442,53 +442,52 @@ PathTextLayout::PathTextLayout(strings::UniString const & text, float fontSize, Init(fribidi::log2vis(text), fontSize, textures); } -void PathTextLayout::CacheStaticGeometry(glm::vec3 const & pivot, - dp::TextureManager::ColorRegion const & colorRegion, +void PathTextLayout::CacheStaticGeometry(dp::TextureManager::ColorRegion const & colorRegion, dp::TextureManager::ColorRegion const & outlineRegion, gpu::TTextOutlinedStaticVertexBuffer & staticBuffer) const { - TextOutlinedGeometryGenerator gen(pivot, colorRegion, outlineRegion, staticBuffer); + TextOutlinedGeometryGenerator gen(colorRegion, outlineRegion, staticBuffer); for_each(m_metrics.begin(), m_metrics.end(), gen); } -void PathTextLayout::CacheStaticGeometry(glm::vec3 const & pivot, dp::TextureManager::ColorRegion const & colorRegion, +void PathTextLayout::CacheStaticGeometry(dp::TextureManager::ColorRegion const & colorRegion, gpu::TTextStaticVertexBuffer & staticBuffer) const { - TextGeometryGenerator gen(pivot, colorRegion, staticBuffer); + TextGeometryGenerator gen(colorRegion, staticBuffer); for_each(m_metrics.begin(), m_metrics.end(), gen); } -bool PathTextLayout::CacheDynamicGeometry(m2::Spline::iterator const & iter, ScreenBase const & screen, +bool PathTextLayout::CacheDynamicGeometry(m2::Spline::iterator const & iter, float depth, + m2::PointD const & globalPivot, gpu::TTextDynamicVertexBuffer & buffer) const { - float const scalePtoG = screen.GetScale(); - float const glbHalfLength = 0.5 * GetPixelLength() * scalePtoG; + float const halfLength = 0.5 * GetPixelLength(); m2::Spline::iterator beginIter = iter; - beginIter.Advance(-glbHalfLength); + beginIter.Advance(-halfLength); m2::Spline::iterator endIter = iter; - endIter.Advance(glbHalfLength); + endIter.Advance(halfLength); if (beginIter.BeginAgain() || endIter.BeginAgain()) return false; float const halfFontSize = 0.5 * GetPixelHeight(); float advanceSign = 1.0f; m2::Spline::iterator penIter = beginIter; - if (screen.GtoP(beginIter.m_pos).x > screen.GtoP(endIter.m_pos).x) + if (beginIter.m_pos.x > endIter.m_pos.x) { advanceSign = -advanceSign; penIter = endIter; } - glsl::vec2 pxPivot = glsl::ToVec2(screen.GtoP(iter.m_pos)); + glsl::vec2 pxPivot = glsl::ToVec2(iter.m_pos); buffer.resize(4 * m_metrics.size()); for (size_t i = 0; i < m_metrics.size(); ++i) { GlyphRegion const & g = m_metrics[i]; m2::PointF pxSize = m2::PointF(g.GetPixelSize()) * m_textSizeRatio; - m2::PointD const pxBase = screen.GtoP(penIter.m_pos); - m2::PointD const pxShiftBase = screen.GtoP(penIter.m_pos + penIter.m_dir); + m2::PointD const pxBase = penIter.m_pos; + m2::PointD const pxShiftBase = penIter.m_pos + penIter.m_dir; glsl::vec2 tangent = advanceSign * glsl::normalize(glsl::ToVec2(pxShiftBase - pxBase)); glsl::vec2 normal = glsl::normalize(glsl::vec2(-tangent.y, tangent.x)); @@ -502,15 +501,15 @@ bool PathTextLayout::CacheDynamicGeometry(m2::Spline::iterator const & iter, Scr size_t baseIndex = 4 * i; - buffer[baseIndex + 0] = gpu::TextDynamicVertex(formingVector + normal * bottomVector + tangent * xOffset); - buffer[baseIndex + 1] = gpu::TextDynamicVertex(formingVector + normal * upVector + tangent * xOffset); - buffer[baseIndex + 2] = gpu::TextDynamicVertex(formingVector + normal * bottomVector + tangent * (pxSize.x + xOffset)); - buffer[baseIndex + 3] = gpu::TextDynamicVertex(formingVector + normal * upVector + tangent * (pxSize.x + xOffset)); - + glsl::vec4 pivot(glsl::ToVec2(globalPivot), depth, 0.0f); + buffer[baseIndex + 0] = gpu::TextDynamicVertex(pivot, formingVector + normal * bottomVector + tangent * xOffset); + buffer[baseIndex + 1] = gpu::TextDynamicVertex(pivot, formingVector + normal * upVector + tangent * xOffset); + buffer[baseIndex + 2] = gpu::TextDynamicVertex(pivot, formingVector + normal * bottomVector + tangent * (pxSize.x + xOffset)); + buffer[baseIndex + 3] = gpu::TextDynamicVertex(pivot, formingVector + normal * upVector + tangent * (pxSize.x + xOffset)); float const xAdvance = g.GetAdvanceX() * m_textSizeRatio; glsl::vec2 currentTangent = glsl::ToVec2(penIter.m_dir); - penIter.Advance(advanceSign * xAdvance * scalePtoG); + penIter.Advance(advanceSign * xAdvance); float const dotProduct = glsl::dot(currentTangent, glsl::ToVec2(penIter.m_dir)); if (dotProduct < kValidSplineTurn) return false; @@ -519,6 +518,41 @@ bool PathTextLayout::CacheDynamicGeometry(m2::Spline::iterator const & iter, Scr return true; } +// static +void PathTextLayout::CalculatePositions(vector & offsets, float splineLength, + float splineScaleToPixel, float textPixelLength) +{ + //we leave a little space on either side of the text that would + //remove the comparison for equality of spline portions + float const kTextBorder = 4.0f; + float const textLength = kTextBorder + textPixelLength; + + // on next readable scale m_scaleGtoP will be twice + if (textLength > splineLength * 2.0 * splineScaleToPixel) + return; + + float const kPathLengthScalar = 0.75; + float const pathLength = kPathLengthScalar * splineScaleToPixel * splineLength; + + float const etalonEmpty = max(300 * df::VisualParams::Instance().GetVisualScale(), (double)textLength); + float const minPeriodSize = etalonEmpty + textLength; + float const twoTextAndEmpty = minPeriodSize + textLength; + + if (pathLength < twoTextAndEmpty) + { + // if we can't place 2 text and empty part on path + // we place only one text on center of path + offsets.push_back(splineLength / 2.0f); + } + else + { + double const textCount = max(floor(pathLength / minPeriodSize), 1.0); + double const glbTextLen = splineLength / textCount; + for (double offset = 0.5 * glbTextLen; offset < splineLength; offset += glbTextLen) + offsets.push_back(offset); + } +} + /////////////////////////////////////////////////////////////// SharedTextLayout::SharedTextLayout(PathTextLayout * layout) : m_layout(layout) diff --git a/drape_frontend/text_layout.hpp b/drape_frontend/text_layout.hpp index 28407b88bc..e01918270c 100644 --- a/drape_frontend/text_layout.hpp +++ b/drape_frontend/text_layout.hpp @@ -62,13 +62,13 @@ public: ref_ptr textures, dp::Anchor anchor); - void Cache(glsl::vec3 const & pivot, glsl::vec2 const & pixelOffset, + void Cache(const glm::vec4 & pivot, glsl::vec2 const & pixelOffset, dp::TextureManager::ColorRegion const & colorRegion, dp::TextureManager::ColorRegion const & outlineRegion, gpu::TTextOutlinedStaticVertexBuffer & staticBuffer, gpu::TTextDynamicVertexBuffer & dynamicBuffer) const; - void Cache(glsl::vec3 const & pivot, glsl::vec2 const & pixelOffset, + void Cache(const glm::vec4 & pivot, glsl::vec2 const & pixelOffset, dp::TextureManager::ColorRegion const & color, gpu::TTextStaticVertexBuffer & staticBuffer, gpu::TTextDynamicVertexBuffer & dynamicBuffer) const; @@ -87,17 +87,19 @@ public: PathTextLayout(strings::UniString const & text, float fontSize, ref_ptr textures); - void CacheStaticGeometry(glsl::vec3 const & pivot, - dp::TextureManager::ColorRegion const & colorRegion, + static void CalculatePositions(vector & offsets, float splineLength, + float splineScaleToPixel, float textPixelLength); + + void CacheStaticGeometry(dp::TextureManager::ColorRegion const & colorRegion, dp::TextureManager::ColorRegion const & outlineRegion, gpu::TTextOutlinedStaticVertexBuffer & staticBuffer) const; - void CacheStaticGeometry(glsl::vec3 const & pivot, - dp::TextureManager::ColorRegion const & colorRegion, + void CacheStaticGeometry(dp::TextureManager::ColorRegion const & colorRegion, gpu::TTextStaticVertexBuffer & staticBuffer) const; bool CacheDynamicGeometry(m2::Spline::iterator const & iter, - ScreenBase const & screen, + float depth, + m2::PointD const & globalPivot, gpu::TTextDynamicVertexBuffer & buffer) const; }; diff --git a/drape_frontend/text_shape.cpp b/drape_frontend/text_shape.cpp index 854b862550..2ec4c14e3d 100644 --- a/drape_frontend/text_shape.cpp +++ b/drape_frontend/text_shape.cpp @@ -22,21 +22,48 @@ namespace class StraightTextHandle : public TextHandle { + using TBase = TextHandle; + public: StraightTextHandle(FeatureID const & id, strings::UniString const & text, dp::Anchor anchor, glsl::vec2 const & pivot, glsl::vec2 const & pxSize, glsl::vec2 const & offset, uint64_t priority, ref_ptr textureManager, - bool isOptional, gpu::TTextDynamicVertexBuffer && normals) - : TextHandle(id, text, anchor, priority, textureManager, move(normals)) + bool isOptional, gpu::TTextDynamicVertexBuffer && normals, + bool isBillboard = false) + : TextHandle(id, text, anchor, priority, textureManager, move(normals), isBillboard) , m_pivot(glsl::ToPoint(pivot)) , m_offset(glsl::ToPoint(offset)) , m_size(glsl::ToPoint(pxSize)) , m_isOptional(isOptional) {} - m2::RectD GetPixelRect(ScreenBase const & screen) const override + m2::PointD GetPivot(ScreenBase const & screen, bool perspective) const override { + m2::PointD pivot = TBase::GetPivot(screen, false); + if (perspective) + pivot = screen.PtoP3d(pivot - m_offset, -m_pivotZ / screen.GetScale()) + m_offset; + return pivot; + } + + m2::RectD GetPixelRect(ScreenBase const & screen, bool perspective) const override + { + if (perspective) + { + if (IsBillboard()) + { + m2::PointD const pxPivot = screen.GtoP(m_pivot); + m2::PointD const pxPivotPerspective = screen.PtoP3d(pxPivot, -m_pivotZ / screen.GetScale()); + + m2::RectD pxRectPerspective = GetPixelRect(screen, false); + pxRectPerspective.Offset(-pxPivot); + pxRectPerspective.Offset(pxPivotPerspective); + + return pxRectPerspective; + } + return GetPixelRectPerspective(screen); + } + m2::PointD pivot = screen.GtoP(m_pivot) + m_offset; double x = pivot.x; double y = pivot.y; @@ -66,9 +93,9 @@ public: max(x, pivot.x), max(y, pivot.y)); } - void GetPixelShape(ScreenBase const & screen, Rects & rects) const override + void GetPixelShape(ScreenBase const & screen, Rects & rects, bool perspective) const override { - rects.push_back(m2::RectF(GetPixelRect(screen))); + rects.emplace_back(GetPixelRect(screen, perspective)); } bool IsBound() const override @@ -154,10 +181,11 @@ void TextShape::DrawSubStringPlain(StraightTextLayout const & layout, dp::FontDe textures->GetColorRegion(font.m_color, color); textures->GetColorRegion(font.m_outlineColor, outline); - layout.Cache(glsl::vec3(glsl::ToVec2(m_basePoint), m_params.m_depth), + layout.Cache(glsl::vec4(glsl::ToVec2(m_basePoint), m_params.m_depth, -m_params.m_posZ), baseOffset, color, staticBuffer, dynamicBuffer); dp::GLState state(gpu::TEXT_PROGRAM, dp::GLState::OverlayLayer); + state.SetProgram3dIndex(gpu::TEXT_BILLBOARD_PROGRAM); ASSERT(color.GetTexture() == outline.GetTexture(), ()); state.SetColorTexture(color.GetTexture()); state.SetMaskTexture(layout.GetMaskTexture()); @@ -175,7 +203,9 @@ void TextShape::DrawSubStringPlain(StraightTextLayout const & layout, dp::FontDe GetOverlayPriority(), textures, isOptional, - move(dynamicBuffer)); + move(dynamicBuffer), + true); + handle->SetPivotZ(m_params.m_posZ); handle->SetOverlayRank(m_hasPOI ? (isPrimary ? dp::OverlayRank1 : dp::OverlayRank2) : dp::OverlayRank0); handle->SetExtendingSize(m_params.m_extendingSize); @@ -196,10 +226,11 @@ void TextShape::DrawSubStringOutlined(StraightTextLayout const & layout, dp::Fon textures->GetColorRegion(font.m_color, color); textures->GetColorRegion(font.m_outlineColor, outline); - layout.Cache(glsl::vec3(glsl::ToVec2(m_basePoint), m_params.m_depth), + layout.Cache(glsl::vec4(glsl::ToVec2(m_basePoint), m_params.m_depth, -m_params.m_posZ), baseOffset, color, outline, staticBuffer, dynamicBuffer); dp::GLState state(gpu::TEXT_OUTLINED_PROGRAM, dp::GLState::OverlayLayer); + state.SetProgram3dIndex(gpu::TEXT_OUTLINED_BILLBOARD_PROGRAM); ASSERT(color.GetTexture() == outline.GetTexture(), ()); state.SetColorTexture(color.GetTexture()); state.SetMaskTexture(layout.GetMaskTexture()); @@ -217,7 +248,9 @@ void TextShape::DrawSubStringOutlined(StraightTextLayout const & layout, dp::Fon GetOverlayPriority(), textures, isOptional, - move(dynamicBuffer)); + move(dynamicBuffer), + true); + handle->SetPivotZ(m_params.m_posZ); handle->SetOverlayRank(m_hasPOI ? (isPrimary ? dp::OverlayRank1 : dp::OverlayRank2) : dp::OverlayRank0); handle->SetExtendingSize(m_params.m_extendingSize); diff --git a/drape_frontend/tile_info.cpp b/drape_frontend/tile_info.cpp index 92dd7bb29f..653eb5779b 100644 --- a/drape_frontend/tile_info.cpp +++ b/drape_frontend/tile_info.cpp @@ -16,6 +16,7 @@ namespace df TileInfo::TileInfo(drape_ptr && context) : m_context(move(context)) + , m_is3dBuildings(false) , m_isCanceled(false) { } @@ -64,7 +65,7 @@ void TileInfo::ReadFeatures(MapDataProvider const & model, MemoryFeatureIndex & ReadFeatureIndex(model); CheckCanceled(); - featuresToRead.reserve(AverageFeaturesCount); + featuresToRead.reserve(kAverageFeaturesCount); memIndex.ReadFeaturesRequest(m_featureInfo, featuresToRead); } @@ -73,7 +74,7 @@ void TileInfo::ReadFeatures(MapDataProvider const & model, MemoryFeatureIndex & RuleDrawer drawer(bind(&TileInfo::InitStylist, this, _1 ,_2), bind(&TileInfo::IsCancelled, this), model.m_isCountryLoadedByNameFn, - make_ref(m_context)); + make_ref(m_context), m_is3dBuildings); model.ReadFeatures(bind(ref(drawer), _1), featuresToRead); } } @@ -99,7 +100,7 @@ void TileInfo::ProcessID(FeatureID const & id) void TileInfo::InitStylist(FeatureType const & f, Stylist & s) { CheckCanceled(); - df::InitStylist(f, m_context->GetTileKey().m_styleZoomLevel, s); + df::InitStylist(f, m_context->GetTileKey().m_styleZoomLevel, m_is3dBuildings, s); } bool TileInfo::DoNeedReadIndex() const diff --git a/drape_frontend/tile_info.hpp b/drape_frontend/tile_info.hpp index 3c5478973c..e2166cfccb 100644 --- a/drape_frontend/tile_info.hpp +++ b/drape_frontend/tile_info.hpp @@ -31,6 +31,9 @@ public: void Cancel(MemoryFeatureIndex & memIndex); bool IsCancelled() const; + void Set3dBuildings(bool buildings3d) { m_is3dBuildings = buildings3d; } + bool Get3dBuildings() const { return m_is3dBuildings; } + m2::RectD GetGlobalRect() const; TileKey const & GetTileKey() const { return m_context->GetTileKey(); } bool operator <(TileInfo const & other) const { return GetTileKey() < other.GetTileKey(); } @@ -47,6 +50,7 @@ private: private: drape_ptr m_context; TFeaturesInfo m_featureInfo; + bool m_is3dBuildings; atomic m_isCanceled; }; diff --git a/drape_frontend/transparent_layer.cpp b/drape_frontend/transparent_layer.cpp new file mode 100644 index 0000000000..3f2268d336 --- /dev/null +++ b/drape_frontend/transparent_layer.cpp @@ -0,0 +1,80 @@ +#include "transparent_layer.hpp" + +#include "drape/data_buffer.hpp" +#include "drape/glconstants.hpp" +#include "drape/glfunctions.hpp" +#include "drape/glstate.hpp" +#include "drape/gpu_program_manager.hpp" +#include "drape/shader_def.hpp" +#include "drape/uniform_values_storage.hpp" + +#include "geometry/screenbase.hpp" + +#include "std/cmath.hpp" + +namespace df +{ + +TransparentLayer::TransparentLayer() +{ + m_vertices = { + -1.0f, 1.0f, 0.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f, + -1.0f, -1.0f, 0.0f, 0.0f, + 1.0f, -1.0f, 1.0f, 0.0f }; +} + +TransparentLayer::~TransparentLayer() +{ + if (m_bufferId != 0) + GLFunctions::glDeleteBuffer(m_bufferId); +} + +void TransparentLayer::Build(ref_ptr prg) +{ + m_attributePosition = prg->GetAttributeLocation("a_pos"); + ASSERT_NOT_EQUAL(m_attributePosition, -1, ()); + + m_attributeTexCoord = prg->GetAttributeLocation("a_tcoord"); + ASSERT_NOT_EQUAL(m_attributeTexCoord, -1, ()); + + m_bufferId = GLFunctions::glGenBuffer(); + GLFunctions::glBindBuffer(m_bufferId, gl_const::GLArrayBuffer); + GLFunctions::glBufferData(gl_const::GLArrayBuffer, m_vertices.size() * sizeof(m_vertices[0]), + m_vertices.data(), gl_const::GLStaticDraw); + GLFunctions::glBindBuffer(0, gl_const::GLArrayBuffer); +} + +void TransparentLayer::Render(uint32_t textureId, ref_ptr mng) +{ + // Unbind current VAO, because glVertexAttributePointer and glEnableVertexAttribute can affect it. + GLFunctions::glBindVertexArray(0); + + ref_ptr prg = mng->GetProgram(gpu::TRANSPARENT_LAYER_PROGRAM); + prg->Bind(); + + if (m_bufferId == 0) + Build(prg); + + GLFunctions::glActiveTexture(gl_const::GLTexture0); + GLFunctions::glBindTexture(textureId); + GLFunctions::glBindBuffer(m_bufferId, gl_const::GLArrayBuffer); + + GLFunctions::glEnableVertexAttribute(m_attributePosition); + GLFunctions::glVertexAttributePointer(m_attributePosition, 2, gl_const::GLFloatType, false, + sizeof(float) * 4, 0); + GLFunctions::glEnableVertexAttribute(m_attributeTexCoord); + GLFunctions::glVertexAttributePointer(m_attributeTexCoord, 2, gl_const::GLFloatType, false, + sizeof(float) * 4, sizeof(float) * 2); + + GLFunctions::glEnable(gl_const::GLBlending); + GLFunctions::glDrawArrays(gl_const::GLTriangleStrip, 0, 4); + GLFunctions::glDisable(gl_const::GLBlending); + + prg->Unbind(); + GLFunctions::glBindTexture(0); + GLFunctions::glBindVertexArray(0); + GLFunctions::glBindBuffer(0, gl_const::GLArrayBuffer); +} + +} // namespace df diff --git a/drape_frontend/transparent_layer.hpp b/drape_frontend/transparent_layer.hpp new file mode 100644 index 0000000000..3ac12a03f4 --- /dev/null +++ b/drape_frontend/transparent_layer.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include "drape/pointers.hpp" + +#include "std/vector.hpp" + +namespace dp +{ +class GpuProgram; +class GpuProgramManager; +} + +class ScreenBase; + +namespace df +{ + +class TransparentLayer +{ +public: + TransparentLayer(); + ~TransparentLayer(); + + void Render(uint32_t textureId, ref_ptr mng); + +private: + void Build(ref_ptr prg); + + uint32_t m_bufferId = 0; + int8_t m_attributePosition; + int8_t m_attributeTexCoord; + + vector m_vertices; +}; + +} // namespace df diff --git a/drape_frontend/user_event_stream.cpp b/drape_frontend/user_event_stream.cpp index 56e8b3bf27..021912a96d 100644 --- a/drape_frontend/user_event_stream.cpp +++ b/drape_frontend/user_event_stream.cpp @@ -1,6 +1,10 @@ #include "drape_frontend/user_event_stream.hpp" #include "drape_frontend/visual_params.hpp" +#include "indexer/scales.hpp" + +#include "platform/platform.hpp" + #include "base/logging.hpp" #include "base/macros.hpp" @@ -138,6 +142,9 @@ ScreenBase const & UserEventStream::ProcessEvents(bool & modelViewChange, bool & bool breakAnim = false; for (UserEvent const & e : events) { + if (m_perspectiveAnimation != nullptr && FilterEventWhile3dAnimation(e.m_type)) + continue; + switch (e.m_type) { case UserEvent::EVENT_SCALE: @@ -155,6 +162,11 @@ ScreenBase const & UserEventStream::ProcessEvents(bool & modelViewChange, bool & TouchCancel(m_touches); break; case UserEvent::EVENT_SET_RECT: + if (m_perspectiveAnimation != nullptr) + { + m_pendingEvent.reset(new UserEvent(e.m_rectEvent)); + break; + } breakAnim = SetRect(e.m_rectEvent.m_rect, e.m_rectEvent.m_zoom, e.m_rectEvent.m_applyRotation, e.m_rectEvent.m_isAnim); TouchCancel(m_touches); break; @@ -169,6 +181,15 @@ ScreenBase const & UserEventStream::ProcessEvents(bool & modelViewChange, bool & { m2::AnyRectD dstRect = GetTargetRect(); dstRect.SetAngle(e.m_rotate.m_targetAzimut); + if (m_navigator.Screen().isPerspective()) + { + ScreenBase const & screen = m_navigator.Screen(); + ScreenBase screenNew = screen; + screenNew.SetAngle(e.m_rotate.m_targetAzimut); + + m2::PointD const screenCenter = screen.P3dtoP(screen.PixelRectIn3d().Center()); + dstRect.Offset(screen.PtoG(screenCenter) - screenNew.PtoG(screenCenter)); + } breakAnim = SetRect(dstRect, true); } break; @@ -178,6 +199,32 @@ ScreenBase const & UserEventStream::ProcessEvents(bool & modelViewChange, bool & e.m_followAndRotate.m_isAnim); TouchCancel(m_touches); break; + case UserEvent::EVENT_ENABLE_PERSPECTIVE: + if (!e.m_enable3dMode.m_immediatelyStart) + m_pendingEvent.reset(new UserEvent(e.m_enable3dMode)); + else + SetEnable3dMode(e.m_enable3dMode.m_rotationAngle, e.m_enable3dMode.m_angleFOV, + e.m_enable3dMode.m_isAnim, viewportChanged); + m_discardedFOV = m_discardedAngle = 0.0; + break; + case UserEvent::EVENT_DISABLE_PERSPECTIVE: + if (m_navigator.Screen().isPerspective()) + SetDisable3dModeAnimation(); + m_discardedFOV = m_discardedAngle = 0.0; + break; + case UserEvent::EVENT_SWITCH_VIEW_MODE: + if (e.m_switchViewMode.m_to2d) + { + m_discardedFOV = m_navigator.Screen().GetAngleFOV(); + m_discardedAngle = m_navigator.Screen().GetRotationAngle(); + SetDisable3dModeAnimation(); + } + else if (m_discardedFOV > 0.0) + { + SetEnable3dMode(m_discardedAngle, m_discardedFOV, true /* isAnim */, viewportChanged); + m_discardedFOV = m_discardedAngle = 0.0; + } + break; default: ASSERT(false, ()); break; @@ -192,11 +239,46 @@ 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_pendingEvent != nullptr && m_pendingEvent->m_type == UserEvent::EVENT_ENABLE_PERSPECTIVE) + { + SetEnable3dMode(m_pendingEvent->m_enable3dMode.m_rotationAngle, + m_pendingEvent->m_enable3dMode.m_angleFOV, + m_pendingEvent->m_enable3dMode.m_isAnim, + viewportChanged); + + m_pendingEvent.reset(); + } m_animation.reset(); + } + } + + if (m_perspectiveAnimation != nullptr) + { + double const angle = m_perspectiveAnimation->GetRotationAngle(); + m_navigator.SetRotationIn3dMode(angle); + modelViewChange = true; + TouchCancel(m_touches); + + if (m_perspectiveAnimation->IsFinished()) + { + if (angle == 0.0) + { + m_navigator.Disable3dMode(); + viewportChanged = true; + + if (m_pendingEvent != nullptr && m_pendingEvent->m_type == UserEvent::EVENT_SET_RECT) + SetRect(m_pendingEvent->m_rectEvent.m_rect, m_pendingEvent->m_rectEvent.m_zoom, + m_pendingEvent->m_rectEvent.m_applyRotation, m_pendingEvent->m_rectEvent.m_isAnim); + } + m_pendingEvent.reset(); + m_perspectiveAnimation.reset(); + } } if (GetValidTouchesCount(m_touches) == 1) @@ -226,13 +308,11 @@ bool UserEventStream::SetScale(m2::PointD const & pxScaleCenter, double factor, // Reset current animation if there is any. ResetCurrentAnimation(); - m2::PointD glbScaleCenter = m_navigator.PtoG(scaleCenter); + m2::PointD glbScaleCenter = m_navigator.PtoG(m_navigator.P3dtoP(scaleCenter)); if (m_listener) m_listener->CorrectGlobalScalePoint(glbScaleCenter); - ScreenBase screen = GetCurrentScreen(); - m_navigator.CalculateScale(scaleCenter, factor, screen); - m2::PointD offset = GetCurrentScreen().PixelRect().Center() - scaleCenter; + m2::PointD const offset = GetCurrentScreen().PixelRect().Center() - m_navigator.P3dtoP(scaleCenter); auto const creator = [this, &glbScaleCenter, &offset](m2::AnyRectD const & startRect, m2::AnyRectD const & endRect, double aDuration, double mDuration, double sDuration) @@ -241,6 +321,9 @@ bool UserEventStream::SetScale(m2::PointD const & pxScaleCenter, double factor, sDuration, glbScaleCenter, offset)); }; + ScreenBase screen = GetCurrentScreen(); + m_navigator.CalculateScale(scaleCenter, factor, screen); + return SetRect(screen.GlobalRect(), true, creator); } @@ -248,15 +331,85 @@ bool UserEventStream::SetScale(m2::PointD const & pxScaleCenter, double factor, return true; } +// static +bool UserEventStream::IsScaleAllowableIn3d(int scale) +{ + int minScale = scales::GetMinAllowableIn3dScale(); + if (df::VisualParams::Instance().GetVisualScale() <= 1.0) + --minScale; + if (GetPlatform().IsTablet()) + ++minScale; + return scale >= minScale; +} + bool UserEventStream::SetCenter(m2::PointD const & center, int zoom, bool isAnim) { - if (zoom == -1) + m2::PointD targetCenter = center; + ang::AngleD angle; + m2::RectD localRect; + + ScreenBase const & currentScreen = GetCurrentScreen(); + + ScreenBase screen = currentScreen; + bool finishIn2d = false; + bool finishIn3d = false; + if (zoom != -1) { - m2::AnyRectD r = GetTargetRect(); - return SetRect(m2::AnyRectD(center, r.Angle(), r.GetLocalRect()), isAnim); + bool const isScaleAllowableIn3d = IsScaleAllowableIn3d(zoom); + finishIn3d = m_discardedFOV > 0.0 && isScaleAllowableIn3d; + finishIn2d = currentScreen.isPerspective() && !isScaleAllowableIn3d; + + if (finishIn3d) + screen.ApplyPerspective(m_discardedAngle, m_discardedAngle, m_discardedFOV); + else if (finishIn2d) + screen.ResetPerspective(); } - return SetRect(df::GetRectForDrawScale(zoom, center), zoom, true, isAnim); + double const scale3d = screen.PixelRect().SizeX() / screen.PixelRectIn3d().SizeX(); + + if (zoom == -1) + { + m2::AnyRectD const r = GetTargetRect(); + angle = r.Angle(); + localRect = r.GetLocalRect(); + } + else + { + angle = screen.GlobalRect().Angle(); + + localRect = df::GetRectForDrawScale(zoom, center); + CheckMinGlobalRect(localRect); + CheckMinMaxVisibleScale(m_isCountryLoaded, localRect, zoom); + + localRect.Offset(-center); + localRect.Scale(scale3d); + + double const aspectRatio = screen.PixelRect().SizeY() / screen.PixelRect().SizeX(); + if (aspectRatio > 1.0) + localRect.Inflate(0.0, localRect.SizeY() / 2.0 * aspectRatio); + else + localRect.Inflate(localRect.SizeX() / 2.0 / aspectRatio, 0.0); + } + + if (screen.isPerspective()) + { + double const centerOffset3d = localRect.SizeY() * (1.0 - 1.0 / (scale3d * cos(screen.GetRotationAngle()))) / 2.0; + targetCenter = targetCenter.Move(centerOffset3d, angle.cos(), -angle.sin()); + } + + if (finishIn2d || finishIn3d) + { + double const scaleToCurrent = + finishIn2d ? currentScreen.PixelRect().SizeX() / currentScreen.PixelRectIn3d().SizeX() + : 1.0 / scale3d; + + double const currentGSizeY = localRect.SizeY() * scaleToCurrent; + targetCenter = targetCenter.Move((currentGSizeY - localRect.SizeY()) / 2.0, + angle.cos(), -angle.sin()); + localRect.Scale(scaleToCurrent); + } + + return SetRect(m2::AnyRectD(targetCenter, angle, localRect), isAnim); } bool UserEventStream::SetRect(m2::RectD rect, int zoom, bool applyRotation, bool isAnim) @@ -351,6 +504,40 @@ bool UserEventStream::SetFollowAndRotate(m2::PointD const & userPos, m2::PointD return true; } +bool UserEventStream::FilterEventWhile3dAnimation(UserEvent::EEventType type) const +{ + return type != UserEvent::EVENT_RESIZE && + type != UserEvent::EVENT_SET_RECT; +} + +void UserEventStream::SetEnable3dMode(double maxRotationAngle, double angleFOV, bool isAnim, bool & viewportChanged) +{ + bool const finishAnimation = m_animation != nullptr && m_animation->GetType() == ModelViewAnimationType::Default; + ResetCurrentAnimation(finishAnimation); + + double const startAngle = isAnim ? 0.0 : maxRotationAngle; + if (isAnim) + { + double const endAngle = maxRotationAngle; + double const rotateDuration = PerspectiveAnimation::GetRotateDuration(startAngle, endAngle); + m_perspectiveAnimation.reset( + new PerspectiveAnimation(rotateDuration, 0.0 /* delay */, startAngle, endAngle)); + } + m_navigator.Enable3dMode(startAngle, maxRotationAngle, angleFOV); + viewportChanged = true; +} + +void UserEventStream::SetDisable3dModeAnimation() +{ + bool const finishAnimation = m_animation != nullptr && m_animation->GetType() == ModelViewAnimationType::Default; + ResetCurrentAnimation(finishAnimation); + + 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) { if (m_animation) @@ -853,4 +1040,9 @@ bool UserEventStream::IsWaitingForActionCompletion() const return m_state != STATE_EMPTY; } +bool UserEventStream::IsInPerspectiveAnimation() const +{ + return m_perspectiveAnimation != nullptr; +} + } diff --git a/drape_frontend/user_event_stream.hpp b/drape_frontend/user_event_stream.hpp index eccf82861c..1327d1e5f2 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" @@ -144,6 +145,34 @@ struct FollowAndRotateEvent bool m_isAnim; }; +struct EnablePerspectiveEvent +{ + EnablePerspectiveEvent(double rotationAngle, double angleFOV, + bool isAnim, bool immediatelyStart) + : m_isAnim(isAnim) + , m_immediatelyStart(immediatelyStart) + , m_rotationAngle(rotationAngle) + , m_angleFOV(angleFOV) + {} + + bool m_isAnim; + bool m_immediatelyStart; + double m_rotationAngle; + double m_angleFOV; +}; + +struct DisablePerspectiveEvent +{ + DisablePerspectiveEvent() {} +}; + +struct SwitchViewModeEvent +{ + SwitchViewModeEvent(bool to2d): m_to2d(to2d) {} + + bool m_to2d; +}; + struct RotateEvent { RotateEvent(double targetAzimut) : m_targetAzimut(targetAzimut) {} @@ -170,7 +199,10 @@ struct UserEvent EVENT_SET_ANY_RECT, EVENT_RESIZE, EVENT_ROTATE, - EVENT_FOLLOW_AND_ROTATE + EVENT_FOLLOW_AND_ROTATE, + EVENT_ENABLE_PERSPECTIVE, + EVENT_DISABLE_PERSPECTIVE, + EVENT_SWITCH_VIEW_MODE }; UserEvent(TouchEvent const & e) : m_type(EVENT_TOUCH) { m_touchEvent = e; } @@ -181,6 +213,9 @@ struct UserEvent UserEvent(ResizeEvent const & e) : m_type(EVENT_RESIZE) { m_resize = e; } 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(EnablePerspectiveEvent const & e) : m_type(EVENT_ENABLE_PERSPECTIVE) { m_enable3dMode = e; } + UserEvent(DisablePerspectiveEvent const & e) : m_type(EVENT_DISABLE_PERSPECTIVE) { m_disable3dMode = e; } + UserEvent(SwitchViewModeEvent const & e) : m_type(EVENT_SWITCH_VIEW_MODE) { m_switchViewMode = e; } EEventType m_type; union @@ -193,6 +228,9 @@ struct UserEvent ResizeEvent m_resize; RotateEvent m_rotate; FollowAndRotateEvent m_followAndRotate; + EnablePerspectiveEvent m_enable3dMode; + DisablePerspectiveEvent m_disable3dMode; + SwitchViewModeEvent m_switchViewMode; }; }; @@ -227,9 +265,11 @@ public: m2::AnyRectD GetTargetRect() const; bool IsInUserAction() const; - + bool IsInPerspectiveAnimation() const; bool IsWaitingForActionCompletion() const; + static bool IsScaleAllowableIn3d(int scale); + void SetListener(ref_ptr listener) { m_listener = listener; } #ifdef DEBUG @@ -262,6 +302,10 @@ private: bool SetFollowAndRotate(m2::PointD const & userPos, m2::PointD const & pixelPos, double azimuth, int preferredZoomLevel, bool isAnim); + bool FilterEventWhile3dAnimation(UserEvent::EEventType type) const; + void SetEnable3dMode(double maxRotationAngle, double angleFOV, bool isAnim, bool & viewportChanged); + void SetDisable3dModeAnimation(); + m2::AnyRectD GetCurrentRect() const; bool ProcessTouch(TouchEvent const & touch); @@ -320,6 +364,12 @@ private: array m_touches; unique_ptr m_animation; + + unique_ptr m_perspectiveAnimation; + unique_ptr m_pendingEvent; + double m_discardedFOV = 0.0; + double m_discardedAngle = 0.0; + ref_ptr m_listener; #ifdef DEBUG diff --git a/drape_frontend/user_mark_shapes.cpp b/drape_frontend/user_mark_shapes.cpp index ed00e3153f..ed1ce38b05 100644 --- a/drape_frontend/user_mark_shapes.cpp +++ b/drape_frontend/user_mark_shapes.cpp @@ -158,6 +158,7 @@ void CacheUserPoints(UserMarksProvider const * provider, } dp::GLState state(gpu::BOOKMARK_PROGRAM, dp::GLState::UserMarkLayer); + state.SetProgram3dIndex(gpu::BOOKMARK_BILLBOARD_PROGRAM); state.SetColorTexture(region.GetTexture()); dp::AttributeProvider attribProvider(1, buffer.size()); diff --git a/drape_head/testing_engine.cpp b/drape_head/testing_engine.cpp index c15b5667f4..2e5f60bc91 100644 --- a/drape_head/testing_engine.cpp +++ b/drape_head/testing_engine.cpp @@ -135,8 +135,8 @@ public: m2::RectF const & rect = region.GetTexRect(); uint32_t length = region.GetMaskPixelLength(); - glsl::vec3 startPos(m_base.x, m_base.y, 0.0f); - glsl::vec3 endPos = startPos + glsl::vec3(length, 0.0f, 0.0f); + glsl::vec4 startPos(m_base.x, m_base.y, 0.0f, 0.0f); + glsl::vec4 endPos = startPos + glsl::vec4(length, 0.0f, 0.0f, 0.0f); gpu::SolidTexturingVertex vertexes[4] = { @@ -172,7 +172,7 @@ public: m2::RectF const & rect = region.GetTexRect(); - glsl::vec3 const basePoint(900.0f, 700.0f, 0.0f); + glsl::vec4 const basePoint(900.0f, 700.0f, 0.0f, 0.0f); float const halfSize = 12.0f; glsl::vec2 texCoord = glsl::ToVec2(rect.Center()); gpu::SolidTexturingVertex vertexes[4] = @@ -508,12 +508,13 @@ void TestingEngine::DrawImpl() { vector trg{ m2::PointD(110.0f, 30.0f), m2::PointD(112.0f, 30.0f), m2::PointD(112.0f, 28.0f), m2::PointD(110.0f, 30.0f), m2::PointD(112.0f, 28.0f), m2::PointD(110.0f, 28.0f) }; + vector edges; AreaViewParams p; p.m_color = dp::Color::White(); p.m_depth = 0.0f; params.m_minVisibleScale = 1; params.m_rank = 0; - AreaShape(move(trg), p).Draw(make_ref(m_batcher), make_ref(m_textures)); + AreaShape(move(trg), move(edges), p).Draw(make_ref(m_batcher), make_ref(m_textures)); } m_batcher->EndSession(); @@ -657,9 +658,9 @@ void TestingEngine::OnFlushData(dp::GLState const & state, drape_ptr handle = m_scene[state].back()->GetOverlayHandle(i); if (handle->Update(m_modelView)) { - m_boundRects.push_back(handle->GetPixelRect(m_modelView)); + m_boundRects.push_back(handle->GetPixelRect(m_modelView, false)); m_rects.resize(m_rects.size() + 1); - handle->GetPixelShape(m_modelView, m_rects.back()); + handle->GetPixelShape(m_modelView, m_rects.back(), false); } }; } diff --git a/geometry/geometry_tests/screen_test.cpp b/geometry/geometry_tests/screen_test.cpp index 943a8cbe67..7e6fdfb721 100644 --- a/geometry/geometry_tests/screen_test.cpp +++ b/geometry/geometry_tests/screen_test.cpp @@ -50,6 +50,58 @@ UNIT_TEST(ScreenBase_P2G2P) TEST(is_equal(pg, screen.PtoG(pp)), ()); } +UNIT_TEST(ScreenBase_3dTransform) +{ + ScreenBase screen; + + double const rotationAngle = math::pi4; + + screen.SetFromRects(m2::AnyRectD(m2::RectD(50, 25, 55, 30)), m2::RectD(0, 0, 200, 400)); + screen.ApplyPerspective(rotationAngle, rotationAngle, math::pi / 3.0); + + TEST(screen.PixelRectIn3d().SizeX() < screen.PixelRect().SizeX(), ()); + TEST(screen.PixelRectIn3d().SizeY() < screen.PixelRect().SizeY(), ()); + + double const kEps = 1.0e-3; + + m2::PointD pp(screen.PixelRect().SizeX() / 2.0, screen.PixelRect().SizeY()); + m2::PointD p3d = screen.PtoP3d(pp); + TEST(p3d.EqualDxDy(m2::PointD(screen.PixelRectIn3d().SizeX() / 2.0, screen.PixelRectIn3d().SizeY()), kEps), ()); + + p3d = m2::PointD(screen.PixelRectIn3d().SizeX() / 2.0, screen.PixelRectIn3d().SizeY() / 2.0); + pp = screen.P3dtoP(p3d); + TEST(pp.EqualDxDy(m2::PointD(screen.PixelRect().SizeX() / 2.0, + screen.PixelRect().SizeY() - screen.PixelRectIn3d().SizeY() / (2.0 * cos(rotationAngle))), kEps), ()); + + p3d = m2::PointD(0, 0); + pp = screen.P3dtoP(p3d); + TEST(pp.x < kEps, ()); + + p3d = m2::PointD(screen.PixelRectIn3d().SizeX(), 0); + pp = screen.P3dtoP(p3d); + TEST(fabs(pp.x - screen.PixelRect().maxX()) < kEps, ()); +} + +UNIT_TEST(ScreenBase_P2P3d2P) +{ + ScreenBase screen; + + screen.SetFromRects(m2::AnyRectD(m2::RectD(50, 25, 55, 30)), m2::RectD(0, 0, 600, 400)); + screen.ApplyPerspective(math::pi4, math::pi4, math::pi / 3.0); + + double const kEps = 1.0e-3; + + // checking that P3dtoP(PtoP3d(p)) == p + m2::PointD pp(500, 300); + m2::PointD p3d = screen.PtoP3d(pp); + TEST(pp.EqualDxDy(screen.P3dtoP(p3d), kEps), ()); + + // checking that PtoP3(P3dtoP(p)) == p + p3d = m2::PointD(400, 300); + pp = screen.P3dtoP(p3d); + TEST(p3d.EqualDxDy(screen.PtoP3d(pp), kEps), ()); +} + UNIT_TEST(ScreenBase_AxisOrientation) { ScreenBase screen; diff --git a/geometry/screenbase.cpp b/geometry/screenbase.cpp index fdd0f8cb8d..c00f882b86 100644 --- a/geometry/screenbase.cpp +++ b/geometry/screenbase.cpp @@ -2,6 +2,7 @@ #include "geometry/transformations.hpp" #include "geometry/angles.hpp" +#include "base/assert.hpp" #include "base/logging.hpp" #include "std/cmath.hpp" @@ -12,6 +13,14 @@ ScreenBase::ScreenBase() : m_Scale(0.1), m_Angle(0.0), m_Org(320, 240), + m_3dFOV(0.0), + m_3dNearZ(0.001), + m_3dFarZ(0.0), + m_3dAngleX(0.0), + m_3dMaxAngleX(0.0), + m_3dScaleX(1.0), + m_3dScaleY(1.0), + m_isPerspective(false), m_GlobalRect(m_Org, ang::AngleD(0), m2::RectD(-320, -240, 320, 240)), m_ClipRect(m2::RectD(0, 0, 640, 480)) { @@ -244,3 +253,161 @@ void ScreenBase::ExtractGtoPParams(MatrixT const & m, dx = m(2, 0); dy = m(2, 1); } + +// Place the camera at the distance, where it gives the same view of plane as the +// orthogonal projection does. Calculate what part of the map would be visible, +// when it is rotated through maxRotationAngle around its near horizontal side. +void ScreenBase::ApplyPerspective(double currentRotationAngle, double maxRotationAngle, double angleFOV) +{ + ASSERT_GREATER(angleFOV, 0.0, ()); + ASSERT_LESS(angleFOV, math::pi2, ()); + ASSERT_GREATER_OR_EQUAL(maxRotationAngle, 0.0, ()); + ASSERT_LESS(maxRotationAngle, math::pi2, ()); + + if (m_isPerspective) + ResetPerspective(); + + m_isPerspective = true; + + 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_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); + + m_PixelRect.setMaxX(m_PixelRect.maxX() * m_3dScaleX); + m_PixelRect.setMaxY(m_PixelRect.maxY() * m_3dScaleY); + + 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_GREATER_OR_EQUAL(rotationAngle, 0.0, ()); + 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; + + Matrix3dT scaleM = math::Identity(); + scaleM(0, 0) = m_3dScaleX; + scaleM(1, 1) = m_3dScaleY; + + Matrix3dT rotateM = math::Identity(); + rotateM(1, 1) = cos(m_3dAngleX); + rotateM(1, 2) = sin(m_3dAngleX); + rotateM(2, 1) = -sin(m_3dAngleX); + rotateM(2, 2) = cos(m_3dAngleX); + + Matrix3dT translateM = math::Identity(); + translateM(3, 1) = offsetY; + translateM(3, 2) = offsetZ; + + Matrix3dT projectionM = math::Zero(); + m_3dFarZ = cameraZ + 2.0 * sin(m_3dAngleX) * m_3dScaleY; + projectionM(0, 0) = projectionM(1, 1) = cameraZ; + 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) = 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); +} + +void ScreenBase::ResetPerspective() +{ + m_isPerspective = false; + + double const dy = m_PixelRect.SizeY() * (1.0 - 1.0 / m_3dScaleX); + + m_PixelRect.setMaxX(m_PixelRect.maxX() / m_3dScaleX); + m_PixelRect.setMaxY(m_PixelRect.maxY() / m_3dScaleY); + + Move(0, -dy / 2.0); + + m_3dScaleX = m_3dScaleY = 1.0; + m_3dAngleX = 0.0; + m_3dMaxAngleX = 0.0; + m_3dFOV = 0.0; +} + +m2::PointD ScreenBase::PtoP3d(m2::PointD const & pt) const +{ + return PtoP3d(pt, 0.0); +} + +m2::PointD ScreenBase::PtoP3d(m2::PointD const & pt, double ptZ) const +{ + if (!m_isPerspective) + return pt; + + Vector3dT const normalizedPoint{float(2.0 * pt.x / m_PixelRect.SizeX() - 1.0), + -float(2.0 * pt.y / m_PixelRect.SizeY() - 1.0), + float(2.0 * ptZ / m_PixelRect.SizeY()), 1.0}; + + Vector3dT const perspectivePoint = normalizedPoint * m_Pto3d; + + m2::RectD const viewport = PixelRectIn3d(); + m2::PointD const pixelPointPerspective( + (perspectivePoint(0, 0) / perspectivePoint(0, 3) + 1.0) * viewport.SizeX() / 2.0, + (-perspectivePoint(0, 1) / perspectivePoint(0, 3) + 1.0) * viewport.SizeY() / 2.0); + + return pixelPointPerspective; +} + +m2::PointD ScreenBase::P3dtoP(m2::PointD const & pt) const +{ + if (!m_isPerspective) + return pt; + + double const normalizedX = 2.0 * pt.x / PixelRectIn3d().SizeX() - 1.0; + double const normalizedY = -2.0 * pt.y / PixelRectIn3d().SizeY() + 1.0; + + double normalizedZ = 0.0; + if (m_3dAngleX != 0.0) + { + double const halfFOV = m_3dFOV / 2.0; + double const cameraZ = 1.0 / tan(halfFOV); + + double const tanX = tan(m_3dAngleX); + double const cameraDistanceZ = + cameraZ * (1.0 + (normalizedY + 1.0) * tanX / (cameraZ - 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); + normalizedZ = a + b / cameraDistanceZ; + } + + Vector3dT const normalizedPoint{normalizedX, normalizedY, normalizedZ, 1.0}; + + Vector3dT const originalPoint = normalizedPoint * m_3dtoP; + + m2::PointD const pixelPointOriginal = + m2::PointD((originalPoint(0, 0) / originalPoint(0, 3) + 1.0) * PixelRect().SizeX() / 2.0, + (-originalPoint(0, 1) / originalPoint(0, 3) + 1.0) * PixelRect().SizeY() / 2.0); + + return pixelPointOriginal; +} diff --git a/geometry/screenbase.hpp b/geometry/screenbase.hpp index eb767e365f..016feab973 100644 --- a/geometry/screenbase.hpp +++ b/geometry/screenbase.hpp @@ -10,7 +10,9 @@ class ScreenBase { public: - typedef math::Matrix MatrixT; + using MatrixT = math::Matrix; + using Matrix3dT = math::Matrix; + using Vector3dT = math::Matrix; private: m2::RectD m_PixelRect; @@ -19,6 +21,15 @@ private: ang::AngleD m_Angle; m2::PointD m_Org; + double m_3dFOV; + double m_3dNearZ; + double m_3dFarZ; + double m_3dAngleX; + double m_3dMaxAngleX; + double m_3dScaleX; + double m_3dScaleY; + bool m_isPerspective; + protected: /// @group Dependent parameters /// Global to Pixel conversion matrix. @@ -39,6 +50,9 @@ protected: /// @} + Matrix3dT m_Pto3d; + Matrix3dT m_3dtoP; + // Update dependent parameters from base parameters. // Must be called when base parameters changed. void UpdateDependentParameters(); @@ -112,6 +126,27 @@ public: m2::AnyRectD const & GlobalRect() const { return m_GlobalRect; } m2::RectD const & ClipRect() const { return m_ClipRect; } + 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; } + bool isPerspective() const { return m_isPerspective; } + + m2::PointD PtoP3d(m2::PointD const & pt) const; + m2::PointD PtoP3d(m2::PointD const & pt, double ptZ) const; + + m2::RectD PixelRectIn3d() const + { + return m2::RectD(0.0, 0.0, m_PixelRect.maxX() / m_3dScaleX, m_PixelRect.maxY() / m_3dScaleY); + } + double GetMinPixelRectSize() const; /// Compute arbitrary pixel transformation, that translates the (oldPt1, oldPt2) -> (newPt1, newPt2) diff --git a/indexer/ftypes_matcher.cpp b/indexer/ftypes_matcher.cpp index 2f65c2fc6c..09adfa9465 100644 --- a/indexer/ftypes_matcher.cpp +++ b/indexer/ftypes_matcher.cpp @@ -199,6 +199,21 @@ IsLocalityChecker::IsLocalityChecker() m_types.push_back(c.GetTypeByPath(vector(arr[i], arr[i] + 2))); } +IsBuildingPartChecker::IsBuildingPartChecker() : BaseChecker(3) +{ +} + +IsBuildingPartChecker const & IsBuildingPartChecker::Instance() +{ + static const IsBuildingPartChecker inst; + return inst; +} + +bool IsBuildingPartChecker::IsMatched(uint32_t type) const +{ + return IsTypeConformed(type, {"building:part"}); +} + IsBridgeChecker::IsBridgeChecker() : BaseChecker(3) { } diff --git a/indexer/ftypes_matcher.hpp b/indexer/ftypes_matcher.hpp index 9d985fe0d5..b439fdfb9b 100644 --- a/indexer/ftypes_matcher.hpp +++ b/indexer/ftypes_matcher.hpp @@ -101,6 +101,14 @@ public: uint32_t GetMainType() const { return m_types[0]; } }; +class IsBuildingPartChecker : public BaseChecker +{ + virtual bool IsMatched(uint32_t type) const; +public: + IsBuildingPartChecker(); + static IsBuildingPartChecker const & Instance(); +}; + class IsBridgeChecker : public BaseChecker { virtual bool IsMatched(uint32_t type) const; diff --git a/indexer/scales.cpp b/indexer/scales.cpp index f3aaeafebf..1396bf285e 100644 --- a/indexer/scales.cpp +++ b/indexer/scales.cpp @@ -10,6 +10,11 @@ namespace scales { static const int INITIAL_LEVEL = 1; + int GetMinAllowableIn3dScale() + { + return min(16, min(GetNavigation3dScale(), GetPedestrianNavigation3dScale())); + } + double GetScaleLevelD(double ratio) { double const level = min(static_cast(GetUpperScale()), log(ratio) / log(2.0) + INITIAL_LEVEL); diff --git a/indexer/scales.hpp b/indexer/scales.hpp index d9bd82a0b7..8a93878779 100644 --- a/indexer/scales.hpp +++ b/indexer/scales.hpp @@ -20,6 +20,12 @@ namespace scales inline int GetNavigationScale() { return UPPER_STYLE_SCALE - 4; } /// Default pedestrian navigation mode scale inline int GetPedestrianNavigationScale() { return UPPER_STYLE_SCALE - 1; } + /// Default navigation 3d mode scale + inline int GetNavigation3dScale() { return UPPER_STYLE_SCALE - 3; } + /// Default pedestrian navigation 3d mode scale + inline int GetPedestrianNavigation3dScale() { return UPPER_STYLE_SCALE - 2; } + + int GetMinAllowableIn3dScale(); double GetScaleLevelD(double ratio); double GetScaleLevelD(m2::RectD const & r); diff --git a/iphone/Maps/Mapsme.storyboard b/iphone/Maps/Mapsme.storyboard index ac18fe12a2..893ee8557d 100644 --- a/iphone/Maps/Mapsme.storyboard +++ b/iphone/Maps/Mapsme.storyboard @@ -1,5 +1,5 @@ - + @@ -61,10 +61,10 @@ - + - + - + - + - + - + - @@ -215,13 +211,11 @@ - @@ -229,7 +223,6 @@ - @@ -243,13 +236,11 @@ - @@ -257,7 +248,6 @@ - @@ -271,13 +261,11 @@ - @@ -285,7 +273,6 @@ - @@ -299,13 +286,11 @@ - @@ -313,7 +298,6 @@ - @@ -327,13 +311,11 @@ - @@ -341,7 +323,6 @@ - @@ -377,10 +358,10 @@ - + - + - + - +