diff --git a/CMakeLists.txt b/CMakeLists.txt index 4357493cef1..118fca877a5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -808,6 +808,16 @@ if(MBGL_WITH_OPENGL) target_sources( mbgl-core PRIVATE + ${PROJECT_SOURCE_DIR}/include/mbgl/gl/location_component_layer.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/gl/location_component_layer_factory.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/location_component_layer_factory.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/location_component_layer.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/location_component_layer_impl.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/location_component_layer_impl.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/location_component_layer_properties.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/location_component_layer_properties.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/render_location_component_layer.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gl/render_location_component_layer.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/gfx/backend.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/gl/custom_layer.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/gl/custom_layer_factory.hpp diff --git a/include/mbgl/gl/location_component_layer.hpp b/include/mbgl/gl/location_component_layer.hpp new file mode 100644 index 00000000000..a4e86211ce0 --- /dev/null +++ b/include/mbgl/gl/location_component_layer.hpp @@ -0,0 +1,111 @@ +#pragma once + +#ifndef GL_GLEXT_PROTOTYPES +#define GL_GLEXT_PROTOTYPES +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace mbgl { +namespace style { + +class LocationComponentLayer : public Layer { +public: + LocationComponentLayer(const std::string& id); + ~LocationComponentLayer() final; + + // Layout properties + + static PropertyValue getDefaultBearingImage(); + const PropertyValue& getBearingImage() const; + void setBearingImage(const PropertyValue&); + + static PropertyValue getDefaultShadowImage(); + const PropertyValue& getShadowImage() const; + void setShadowImage(const PropertyValue&); + + static PropertyValue getDefaultTopImage(); + const PropertyValue& getTopImage() const; + void setTopImage(const PropertyValue&); + + static PropertyValue > getDefaultLocation(); + const PropertyValue >& getLocation() const; + void setLocation(const PropertyValue >&); + void setLocation(const LatLng& crd); // a sane overload for LatLng + + static PropertyValue getDefaultAccuracyRadius(); + const PropertyValue& getAccuracyRadius() const; + void setAccuracyRadius(const PropertyValue&); + + static PropertyValue getDefaultBearing(); + const PropertyValue& getBearing() const; + void setBearing(const PropertyValue&); + + static PropertyValue getDefaultBearingImageSize(); + const PropertyValue& getBearingImageSize() const; + void setBearingImageSize(const PropertyValue&); + + static PropertyValue getDefaultImageTiltDisplacement(); + const PropertyValue& getImageTiltDisplacement() const; + void setImageTiltDisplacement(const PropertyValue&); + + static PropertyValue getDefaultPerspectiveCompensation(); + const PropertyValue& getPerspectiveCompensation() const; + void setPerspectiveCompensation(const PropertyValue&); + + static PropertyValue getDefaultShadowImageSize(); + const PropertyValue& getShadowImageSize() const; + void setShadowImageSize(const PropertyValue&); + + static PropertyValue getDefaultTopImageSize(); + const PropertyValue& getTopImageSize() const; + void setTopImageSize(const PropertyValue&); + + // Paint properties + + static PropertyValue getDefaultAccuracyRadiusBorderColor(); + const PropertyValue& getAccuracyRadiusBorderColor() const; + void setAccuracyRadiusBorderColor(const PropertyValue&); + void setAccuracyRadiusBorderColorTransition(const TransitionOptions&); + TransitionOptions getAccuracyRadiusBorderColorTransition() const; + + static PropertyValue getDefaultAccuracyRadiusColor(); + const PropertyValue& getAccuracyRadiusColor() const; + void setAccuracyRadiusColor(const PropertyValue&); + void setAccuracyRadiusColorTransition(const TransitionOptions&); + TransitionOptions getAccuracyRadiusColorTransition() const; + + // Private implementation + class Impl; + const Impl& impl() const; + + Mutable mutableImpl() const; + LocationComponentLayer(Immutable); + std::unique_ptr cloneRef(const std::string& id) const final; + + // Dynamic properties + optional setPropertyInternal(const std::string& name, + const conversion::Convertible& value) final; + + StyleProperty getProperty(const std::string& name) const final; + Value serialize() const final; + + Mutable mutableBaseImpl() const final; +}; +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/gl/location_component_layer_factory.hpp b/include/mbgl/gl/location_component_layer_factory.hpp new file mode 100644 index 00000000000..160a652cca3 --- /dev/null +++ b/include/mbgl/gl/location_component_layer_factory.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include + +namespace mbgl { + +class LocationComponentLayerFactory : public LayerFactory { +protected: + const style::LayerTypeInfo* getTypeInfo() const noexcept final; + std::unique_ptr createLayer(const std::string& id, + const style::conversion::Convertible& value) noexcept final; + std::unique_ptr createRenderLayer(Immutable) noexcept final; +}; + +} // namespace mbgl diff --git a/platform/default/src/mbgl/layermanager/layer_manager.cpp b/platform/default/src/mbgl/layermanager/layer_manager.cpp index f9e46d78abf..3d0e1fd5cfb 100644 --- a/platform/default/src/mbgl/layermanager/layer_manager.cpp +++ b/platform/default/src/mbgl/layermanager/layer_manager.cpp @@ -4,6 +4,7 @@ #include #ifdef MBGL_RENDER_BACKEND_OPENGL #include +#include #endif #include #include @@ -65,6 +66,9 @@ LayerManagerDefault::LayerManagerDefault() { #if !defined(MBGL_LAYER_CUSTOM_DISABLE_ALL) addLayerType(std::make_unique()); #endif +#if !defined(MBGL_LAYER_LOCATION_COMPONENT_DISABLE_ALL) + addLayerType(std::make_unique()); +#endif #endif } diff --git a/platform/glfw/CMakeLists.txt b/platform/glfw/CMakeLists.txt index dd7fd306a0c..2218571be85 100644 --- a/platform/glfw/CMakeLists.txt +++ b/platform/glfw/CMakeLists.txt @@ -13,6 +13,11 @@ add_executable( ${PROJECT_SOURCE_DIR}/platform/default/src/mbgl/map/map_snapshotter.cpp ) +set_property( + SOURCE ${PROJECT_SOURCE_DIR}/platform/glfw/glfw_view.cpp + PROPERTY COMPILE_DEFINITIONS MAPBOX_PUCK_ASSETS_PATH=\"${PROJECT_SOURCE_DIR}/platform/glfw/assets/\" +) + if(MBGL_WITH_OPENGL) target_sources( mbgl-glfw diff --git a/platform/glfw/assets/puck.png b/platform/glfw/assets/puck.png new file mode 100644 index 00000000000..09d93ebec86 Binary files /dev/null and b/platform/glfw/assets/puck.png differ diff --git a/platform/glfw/assets/puck_hat.png b/platform/glfw/assets/puck_hat.png new file mode 100644 index 00000000000..e9411e0d022 Binary files /dev/null and b/platform/glfw/assets/puck_hat.png differ diff --git a/platform/glfw/assets/puck_shadow.png b/platform/glfw/assets/puck_shadow.png new file mode 100644 index 00000000000..baf848b597d Binary files /dev/null and b/platform/glfw/assets/puck_shadow.png differ diff --git a/platform/glfw/glfw_view.cpp b/platform/glfw/glfw_view.cpp index 332ac7c0fb4..cab2a7e7a0f 100644 --- a/platform/glfw/glfw_view.cpp +++ b/platform/glfw/glfw_view.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -28,6 +29,9 @@ #include #include +// glif this one +#include + #if MBGL_USE_GLES2 #define GLFW_INCLUDE_ES2 #endif // MBGL_USE_GLES2 @@ -41,6 +45,10 @@ #include #include +namespace { +const std::string mbglPuckAssetsPath{MAPBOX_PUCK_ASSETS_PATH}; +} + class SnapshotObserver final : public mbgl::MapSnapshotterObserver { public: ~SnapshotObserver() override = default; @@ -459,6 +467,9 @@ void GLFWView::onKey(GLFWwindow *window, int key, int /*scancode*/, int action, // Snapshot with overlay view->makeSnapshot(true); } break; + case GLFW_KEY_G: { + view->toggleLocationComponentLayer(); + } break; } } @@ -689,6 +700,11 @@ void GLFWView::onScroll(GLFWwindow *window, double /*xOffset*/, double yOffset) } view->map->scaleBy(scale, mbgl::ScreenCoordinate { view->lastX, view->lastY }); + + if (view->puck && view->puckFollowsCameraCenter) { + mbgl::LatLng mapCenter = view->map->getCameraOptions().center.value(); + view->puck->setLocation(mapCenter); + } } void GLFWView::onWindowResize(GLFWwindow *window, int width, int height) { @@ -756,6 +772,11 @@ void GLFWView::onMouseMove(GLFWwindow *window, double x, double y) { view->lastX = x; view->lastY = y; + if (view->puck && view->puckFollowsCameraCenter) { + mbgl::LatLng mapCenter = view->map->getCameraOptions().center.value(); + view->puck->setLocation(mapCenter); + } + auto &style = view->map->getStyle(); if (style.getLayer("state-fills")) { auto screenCoordinate = mbgl::ScreenCoordinate{view->lastX, view->lastY}; @@ -879,6 +900,8 @@ void GLFWView::setWindowTitle(const std::string& title) { } void GLFWView::onDidFinishLoadingStyle() { + puck = nullptr; + if (show3DExtrusions) { toggle3DExtrusions(show3DExtrusions); } @@ -933,3 +956,64 @@ void GLFWView::toggleCustomSource() { mbgl::style::VisibilityType::None : mbgl::style::VisibilityType::Visible); } } + +void GLFWView::toggleLocationComponentLayer() { + puck = static_cast(map->getStyle().getLayer("puck")); + if (puck == nullptr) { + auto puckLayer = std::make_unique("puck"); + + puckLayer->setBearing(0); + puckLayer->setLocation(mbgl::LatLng{35.683389, 139.76525}); + puckLayer->setAccuracyRadius(50); + puckLayer->setAccuracyRadiusColor(mbgl::Color{0.0, 1.0, 0.0, 0.05}); + puckLayer->setAccuracyRadiusBorderColor(mbgl::Color{0.0, 1.0, 0.0, 0.25}); + puckLayer->setTopImageSize(24); + puckLayer->setBearingImageSize(72); + puckLayer->setShadowImageSize(96); + puckLayer->setImageTiltDisplacement(8.0f); // set to 0 for a "flat" puck + puckLayer->setPerspectiveCompensation(0.9); + + map->getStyle().addImage(std::make_unique( + "puck.png", mbgl::decodeImage(mbgl::util::read_file(mbglPuckAssetsPath + "puck.png")), 1.0)); + + map->getStyle().addImage(std::make_unique( + "puck_shadow.png", mbgl::decodeImage(mbgl::util::read_file(mbglPuckAssetsPath + "puck_shadow.png")), 1.0)); + + map->getStyle().addImage(std::make_unique( + "puck_hat.png", mbgl::decodeImage(mbgl::util::read_file(mbglPuckAssetsPath + "puck_hat.png")), 1.0)); + + puckLayer->setBearingImage(mbgl::style::expression::Image("puck.png")); + puckLayer->setShadowImage(mbgl::style::expression::Image("puck_shadow.png")); + puckLayer->setTopImage(mbgl::style::expression::Image("puck_hat.png")); + + puck = puckLayer.get(); + map->getStyle().addLayer(std::move(puckLayer)); + } else { + bool visible = puck->getVisibility() == mbgl::style::VisibilityType::Visible; + if (visible) { + if (!puckFollowsCameraCenter) { + mbgl::LatLng mapCenter = map->getCameraOptions().center.value(); + puck->setLocation(mapCenter); + puckFollowsCameraCenter = true; + } else { + puckFollowsCameraCenter = false; + puck->setVisibility(mbgl::style::VisibilityType(mbgl::style::VisibilityType::None)); + } + } else { + puck->setLocation(mbgl::LatLng{35.683389, 139.76525}); + puck->setVisibility(mbgl::style::VisibilityType(mbgl::style::VisibilityType::Visible)); + puckFollowsCameraCenter = false; + } + } +} + +using Nanoseconds = std::chrono::nanoseconds; + +void GLFWView::onWillStartRenderingFrame() { + puck = static_cast(map->getStyle().getLayer("puck")); + if (puck) { + uint64_t ns = mbgl::Clock::now().time_since_epoch().count(); + const float bearing = float(ns % 2000000000) / 2000000000.0 * 360.0; + puck->setBearing(bearing); + } +} diff --git a/platform/glfw/glfw_view.hpp b/platform/glfw/glfw_view.hpp index 21b54f87a9c..2ef87029004 100644 --- a/platform/glfw/glfw_view.hpp +++ b/platform/glfw/glfw_view.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -61,6 +62,7 @@ class GLFWView : public mbgl::MapObserver { // mbgl::MapObserver implementation void onDidFinishLoadingStyle() override; + void onWillStartRenderingFrame() override; protected: // mbgl::Backend implementation @@ -91,6 +93,7 @@ class GLFWView : public mbgl::MapObserver { void addAnimatedAnnotation(); void updateAnimatedAnnotations(); void toggleCustomSource(); + void toggleLocationComponentLayer(); void cycleDebugOptions(); void clearAnnotations(); @@ -148,4 +151,6 @@ class GLFWView : public mbgl::MapObserver { std::unique_ptr snapshotter; std::unique_ptr snapshotterObserver; mbgl::ResourceOptions mapResourceOptions; + mbgl::style::LocationComponentLayer *puck = nullptr; + bool puckFollowsCameraCenter = false; }; diff --git a/scripts/generate-style-code.js b/scripts/generate-style-code.js index 804d4a243f8..08fd1fba195 100755 --- a/scripts/generate-style-code.js +++ b/scripts/generate-style-code.js @@ -64,7 +64,7 @@ global.evaluatedType = function (property) { case 'boolean': return 'bool'; case 'number': - return 'float'; + return /location$/.test(property.name) ? 'double' : 'float'; case 'resolvedImage': return 'expression::Image'; case 'formatted': @@ -77,7 +77,7 @@ global.evaluatedType = function (property) { return `Color`; case 'array': if (property.length) { - return `std::array<${evaluatedType({type: property.value})}, ${property.length}>`; + return `std::array<${evaluatedType({type: property.value, name: property.name})}, ${property.length}>`; } else { return `std::vector<${evaluatedType({type: property.value, name: property.name})}>`; } diff --git a/scripts/style-spec.js b/scripts/style-spec.js index 8a9c9d4144c..b8c6de775f4 100644 --- a/scripts/style-spec.js +++ b/scripts/style-spec.js @@ -1 +1,162 @@ -var spec = module.exports = require('../mapbox-gl-js/src/style-spec/reference/v8'); +const referenceSpec = require('../mapbox-gl-js/src/style-spec/reference/v8'); + +referenceSpec.layer.type.values["location-component"] = {}; +referenceSpec["layout_location-component"] = { + "top-image": { + "type": "resolvedImage", + "property-type": "data-constant", + "expression": { + "interpolated": false, + "parameters": [ + "zoom" + ] + } + }, + "bearing-image": { + "type": "resolvedImage", + "property-type": "data-constant", + "expression": { + "interpolated": false, + "parameters": [ + "zoom" + ] + } + }, + "shadow-image": { + "type": "resolvedImage", + "property-type": "data-constant", + "expression": { + "interpolated": false, + "parameters": [ + "zoom" + ] + } + }, + "location": { + "type": "array", + "default": [ + 0.0, + 0.0, + 0.0 + ], + "length": 3, + "value": "number", + "property-type": "data-constant", + "transition": false, + "expression": { + "interpolated": false, + "parameters": [] + } + }, + "bearing": { + "type": "number", + "default": "0", + "property-type": "data-constant", + "transition": false, + "expression": { + "interpolated": false, + "parameters": [ ] + } + }, + "accuracy-radius": { + "type": "number", + "default": "0", + "property-type": "data-constant", + "transition": true, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + } + }, + "top-image-size": { + "type": "number", + "property-type": "data-constant", + "default": "1", + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "transition": true + }, + "bearing-image-size": { + "type": "number", + "property-type": "data-constant", + "default": "1", + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "transition": true + }, + "shadow-image-size": { + "type": "number", + "property-type": "data-constant", + "default": "1", + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "transition": true + }, + "perspective-compensation": { + "type": "number", + "default": "0.85", + "property-type": "data-constant", + "transition": false, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + } + }, + "image-tilt-displacement": { + "type": "number", + "property-type": "data-constant", + "default": "0", + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "transition": true + } +}; + +referenceSpec["paint_location-component"] = { + "accuracy-radius-color": { + "type": "color", + "property-type": "data-constant", + "default": "#ffffff", + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "transition": true + }, + "accuracy-radius-border-color": { + "type": "color", + "property-type": "data-constant", + "default": "#ffffff", + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + }, + "transition": true + } +}; + +var spec = module.exports = referenceSpec diff --git a/src/mbgl/gl/defines.hpp b/src/mbgl/gl/defines.hpp index 75325dfb75a..1d9177d069c 100644 --- a/src/mbgl/gl/defines.hpp +++ b/src/mbgl/gl/defines.hpp @@ -93,6 +93,7 @@ #define GL_LESS 0x0201 #define GL_LINEAR 0x2601 #define GL_LINEAR_MIPMAP_NEAREST 0x2701 +#define GL_LINEAR_MIPMAP_LINEAR 0x2703 #define GL_LINE_LOOP 0x0002 #define GL_LINES 0x0001 #define GL_LINE_STRIP 0x0003 @@ -102,6 +103,7 @@ #define GL_MAX_VERTEX_ATTRIBS 0x8869 #define GL_NEAREST 0x2600 #define GL_NEAREST_MIPMAP_NEAREST 0x2700 +#define GL_NEAREST_MIPMAP_LINEAR 0x2702 #define GL_NEVER 0x0200 #define GL_NO_ERROR 0 #define GL_NOTEQUAL 0x0205 @@ -161,7 +163,7 @@ #define GL_VERTEX_SHADER 0x8B31 #define GL_VIEWPORT 0x0BA2 #define GL_ZERO 0 - +#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE #ifdef MBGL_USE_GLES2 #define GL_HALF_FLOAT 0x8D61 #else @@ -176,4 +178,4 @@ #define GL_RGBA8 0x8058 #define GL_ZOOM_X 0x0D16 #define GL_ZOOM_Y 0x0D17 -#endif \ No newline at end of file +#endif diff --git a/src/mbgl/gl/location_component_layer.cpp b/src/mbgl/gl/location_component_layer.cpp new file mode 100644 index 00000000000..3e37a37c46b --- /dev/null +++ b/src/mbgl/gl/location_component_layer.cpp @@ -0,0 +1,1245 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace mbgl::platform; +namespace mbgl { +namespace style { +namespace conversion { +template <> +struct Converter>, void> { + optional>> operator()(const Convertible& value, + Error& error, + bool, + bool) const { + optional> a = convert>(value, error); + + if (!a) { + return nullopt; + } + std::array res; + res[0] = (*a)[0]; + res[1] = (*a)[1]; + res[2] = (*a)[2]; + + PropertyValue> r(res); + return r; + } +}; +} // namespace conversion + +class PuckLayerHost : public mbgl::style::LocationComponentLayerHost { +protected: + struct vec2 { + GLfloat x = 0.0f; + GLfloat y = 0.0f; + + vec2(GLfloat x_, GLfloat y_) : x(x_), y(y_) {} + vec2() {} + vec2(const Point& p) : x(p.x), y(p.y) {} + vec2(const vec2& o) : x(o.x), y(o.y) {} + vec2(const vec2&& o) : x(o.x), y(o.y) {} + vec2& operator=(vec2&& o) { + x = o.x; + y = o.y; + return *this; + } + vec2& operator=(vec2& o) { + x = o.x; + y = o.y; + return *this; + } + float length() const { return std::sqrt(x * x + y * y); } + vec2 normalized() const { + const float size = length(); + return vec2(x / size, y / size); + } + void normalize() { *this = normalized(); } + vec2 mirrored(const vec2& mirror) const { + float k = dot(mirror) / mirror.length(); + return 2.0 * k * mirror - (*this); + } + float dot(const vec2& v2) const { return x * v2.x + y * v2.y; } + vec2 rotated(float degrees) const { + const float cs = std::cos(degrees * util::DEG2RAD); + const float sn = std::sin(degrees * util::DEG2RAD); + return vec2{x * cs + y * sn, x * sn + y * cs}.normalized(); + } + float bearing() const { + const vec2 norm = normalized(); + + // From theta to bearing + return util::wrap(M_PI_2 - std::atan2(-norm.y, norm.x), 0, M_PI * 2.0) * util::RAD2DEG; + } + Point point() const { return {x, y}; } + + friend vec2 operator-(const vec2& v) { return vec2(-v.x, -v.y); } + friend vec2 operator*(double a, const vec2& v) { return vec2(v.x * a, v.y * a); } + friend vec2 operator+(const vec2& v1, const vec2& v2) { return vec2(v1.x + v2.x, v1.y + v2.y); } + friend vec2 operator-(const vec2& v1, const vec2& v2) { return vec2(v1.x - v2.x, v1.y - v2.y); } + }; + + struct Shader { + virtual ~Shader() { release(); } + void release() { + if (!program) return; + MBGL_CHECK_ERROR(glDetachShader(program, vertexShader)); + MBGL_CHECK_ERROR(glDetachShader(program, fragmentShader)); + MBGL_CHECK_ERROR(glDeleteShader(vertexShader)); + MBGL_CHECK_ERROR(glDeleteShader(fragmentShader)); + MBGL_CHECK_ERROR(glDeleteProgram(program)); + program = vertexShader = fragmentShader = 0; + } + void initialize(const GLchar* const& vsSource, const GLchar* const& fsSource) { + if (program) return; + program = MBGL_CHECK_ERROR(glCreateProgram()); + vertexShader = MBGL_CHECK_ERROR(glCreateShader(GL_VERTEX_SHADER)); + fragmentShader = MBGL_CHECK_ERROR(glCreateShader(GL_FRAGMENT_SHADER)); + MBGL_CHECK_ERROR(glShaderSource(vertexShader, 1, &vsSource, nullptr)); + MBGL_CHECK_ERROR(glCompileShader(vertexShader)); + MBGL_CHECK_ERROR(glAttachShader(program, vertexShader)); + MBGL_CHECK_ERROR(glShaderSource(fragmentShader, 1, &fsSource, nullptr)); + MBGL_CHECK_ERROR(glCompileShader(fragmentShader)); + MBGL_CHECK_ERROR(glAttachShader(program, fragmentShader)); + MBGL_CHECK_ERROR(glLinkProgram(program)); + pullLocations(); + } + virtual void bind() { MBGL_CHECK_ERROR(glUseProgram(program)); } + void detach() { MBGL_CHECK_ERROR(glUseProgram(0)); } + virtual void pullLocations(){}; + + GLuint program = 0; + GLuint vertexShader = 0; + GLuint fragmentShader = 0; + }; + + struct SimpleShader : public Shader { + // Note that custom layers need to draw geometry with a z value of 1 to take advantage of + // depth-based fragment culling. + const GLchar* vertexShaderSource = R"MBGL_SHADER( +attribute vec2 a_pos; +uniform mat4 u_matrix; +void main() { + gl_Position = u_matrix * vec4(a_pos, 0, 1); +// gl_Position = vec4(a_pos, 0, 1); +} +)MBGL_SHADER"; + + const GLchar* fragmentShaderSource = R"MBGL_SHADER( +uniform vec4 u_color; +void main() { + gl_FragColor = u_color; +} +)MBGL_SHADER"; + + void initialize() { Shader::initialize(SimpleShader::vertexShaderSource, SimpleShader::fragmentShaderSource); } + + void pullLocations() override { + a_pos = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_pos")); + u_color = MBGL_CHECK_ERROR(glGetUniformLocation(program, "u_color")); + u_matrix = MBGL_CHECK_ERROR(glGetUniformLocation(program, "u_matrix")); + } + void bind() override { + SimpleShader::initialize(); + Shader::bind(); + } + + GLuint a_pos = 0; + GLuint u_color = 0; + GLuint u_matrix = 0; + }; + + struct TexturedShader : public Shader { + const GLchar* vertexShaderSource = R"MBGL_SHADER( +#ifdef GL_ES +precision highp float; +#endif + +attribute vec2 a_pos; +attribute vec2 a_texCoord; +uniform mat4 u_matrix; +varying vec2 v_texCoord; +void main() { + gl_Position = u_matrix * vec4(a_pos, 0, 1); + v_texCoord = a_texCoord; +} +)MBGL_SHADER"; + + const GLchar* fragmentShaderSource = R"MBGL_SHADER( +#ifdef GL_ES +precision mediump float; +#endif + +uniform sampler2D u_image; +varying vec2 v_texCoord; +void main() { + vec4 color = texture2D(u_image, v_texCoord); + gl_FragColor = color; +} +)MBGL_SHADER"; + + void initialize() { Shader::initialize(vertexShaderSource, fragmentShaderSource); } + + void pullLocations() override { + a_pos = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_pos")); + a_texCoord = MBGL_CHECK_ERROR(glGetAttribLocation(program, "a_texCoord")); + u_image = MBGL_CHECK_ERROR(glGetUniformLocation(program, "u_image")); + u_matrix = MBGL_CHECK_ERROR(glGetUniformLocation(program, "u_matrix")); + } + void bind() override { + TexturedShader::initialize(); + Shader::bind(); + } + + GLuint a_pos = 0; + GLuint a_texCoord = 0; + GLuint u_image = 0; + GLuint u_matrix = 0; + }; + + struct Buffer { + virtual ~Buffer() { release(); } + void release() { + if (!bufferId) return; + MBGL_CHECK_ERROR(glDeleteBuffers(1, &bufferId)); + bufferId = 0; + } + void initialize() { + if (!bufferId) MBGL_CHECK_ERROR(glGenBuffers(1, &bufferId)); + } + void bind(const GLenum target = GL_ARRAY_BUFFER) { + initialize(); + MBGL_CHECK_ERROR(glBindBuffer(target, bufferId)); + } + void detach(const GLenum target = GL_ARRAY_BUFFER) { MBGL_CHECK_ERROR(glBindBuffer(target, 0)); } + template + void upload(const std::array& data) { + bind(); + MBGL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, N * sizeof(T), data.data(), GL_STATIC_DRAW)); + size = uint(N * sizeof(T)); + elements = N; + } + template + void upload(const std::vector& data) { + bind(); + MBGL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, data.size() * sizeof(T), data.data(), GL_STATIC_DRAW)); + size = data.size() * sizeof(T); + elements = data.size(); + } + + GLuint bufferId = 0; + unsigned int size = 0; + unsigned int elements = 0; + }; + +public: + struct Texture { + ~Texture() { release(); } + void release() { + MBGL_CHECK_ERROR(glDeleteTextures(1, &texId)); + texId = 0; + } + /* + Assign can be called any time. Conversely, upload must be called with a bound gl context. + */ + void assign(const mbgl::PremultipliedImage* img) { + if (img == image) return; + imageDirty = true; + image = img; + } + + void upload() { + if (!imageDirty) return; + imageDirty = false; + initialize(); + + MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texId)); + if (!image || !image->valid()) { + MBGL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0)); + } else { + MBGL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, + 0, + GL_RGBA, + image->size.width, + image->size.height, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + image->data.get())); + MBGL_CHECK_ERROR(glGenerateMipmap(GL_TEXTURE_2D)); + MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR)); + MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + // ToDo: check if this extension is available before using it. + MBGL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16)); + } + detach(); + } + void initialize() { + if (texId != 0) return; + MBGL_CHECK_ERROR(glGenTextures(1, &texId)); + } + void bind(int textureUnit = -1) { + initialize(); + if (!image && !imageDirty) return; + + upload(); + if (textureUnit >= 0) MBGL_CHECK_ERROR(glActiveTexture(GL_TEXTURE0 + textureUnit)); + MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texId)); + } + void detach() { MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, 0)); } + bool isValid() { return imageDirty || image; } + GLuint texId = 0; + const mbgl::PremultipliedImage* image = nullptr; + bool imageDirty = false; + }; + + PuckLayerHost() : ruler(0, mapbox::cheap_ruler::CheapRuler::Meters) {} + + void initialize() override { + simpleShader.initialize(); + texturedShader.initialize(); + texCoords = {{{0.0f, 1.0f}, + {0.0f, 0.0f}, + {1.0f, 0.0f}, + {1.0f, 1.0f}}}; // Quads will be drawn as triangle fans. so bl, tl, tr, br + texCoordsBuffer.upload(texCoords); + matrix::identity(projectionPuck); + projectionCircle = projectionPuck; + } + + void render(const mbgl::style::LocationComponentRenderParameters& params) override { + updatePuckGeometry(params); + drawRadius(params); + drawShadow(); + drawPuck(); + drawHat(); + } + + void contextLost() override {} + + void deinitialize() override { + if (!simpleShader.program) return; + textures.clear(); + buffer.release(); + circleBuffer.release(); + puckBuffer.release(); + hatBuffer.release(); + texCoordsBuffer.release(); + simpleShader.release(); + texturedShader.release(); + } + +protected: + static void toMercator(mat4& mbgl_proj, const double zoom, const double latitude) { + // Scale by worldSize so that the values in the input coodinate system are between 0 and 1. + const double worldSize = std::pow(2, zoom) * 512; + for (size_t i = 0; i < 12; i++) { + mbgl_proj.at(i) *= worldSize; + } + + // Undo the z scaling done in GL native that makes z coordinates match altitude in meters. This makes the + // projection conformant, i.e., a cube in the input coordinate system is a cube in the output. + double zScale = mbgl::Projection::getMetersPerPixelAtLatitude(latitude, zoom); + for (size_t i = 8; i < 12; i++) { + mbgl_proj.at(i) *= zScale; + } + } + + void updatePuckGeometry(const mbgl::style::LocationComponentRenderParameters& params) { + if (params.projectionMatrix != oldParams.projectionMatrix) positionChanged = true; + if (params.puckPosition != oldParams.puckPosition) { + positionChanged = true; + ruler = mapbox::cheap_ruler::CheapRuler(params.puckPosition.latitude(), + mapbox::cheap_ruler::CheapRuler::Meters); + } + if (params.puckBearing != oldParams.puckBearing) bearingChanged = true; + if (params.errorRadiusMeters != oldParams.errorRadiusMeters) radiusChanged = true; + if (params.puckImagePath != oldParams.puckImagePath) setTextureFromFile(params.puckImagePath, texPuck, params); + if (params.puckShadowImagePath != oldParams.puckShadowImagePath) + setTextureFromFile(params.puckShadowImagePath, texShadow, params); + if (params.puckHatImagePath != oldParams.puckHatImagePath) + setTextureFromFile(params.puckHatImagePath, texPuckHat, params); + + const TransformState& s = *params.state; + const double scale = s.getScale(); + projectionCircle = params.projectionMatrix; + const Point positionMercator = Projection::project(params.puckPosition, scale); + const Point diff = positionMercator; + mat4 translation; + matrix::identity(translation); + matrix::translate(translation, translation, diff.x, diff.y, 0.0); + matrix::multiply(projectionCircle, projectionCircle, translation); + + if (positionChanged) { + updateRadius(params); + updatePuck(params); + positionChanged = false; + } else { + if (radiusChanged) { + updateRadius(params); + } + if (bearingChanged) { + updatePuck(params); + } + } + oldParams = params; + } + + void updateRadius(const mbgl::style::LocationComponentRenderParameters& params) { + const TransformState& s = *params.state; + const unsigned long numVtxCircumference = circle.size() - 1; + const float bearingStep = 360.0f / float(numVtxCircumference - 1); // first and last points are the same + const mapbox::cheap_ruler::point centerPoint(params.puckPosition.longitude(), params.puckPosition.latitude()); + const double scale = s.getScale(); + Point center = Projection::project(params.puckPosition, scale); + circle[0] = {0, 0}; + + double mapBearing = util::wrap(util::RAD2DEG * params.bearing, 0.0, util::DEGREES_MAX); + for (unsigned long i = 1; i <= numVtxCircumference; ++i) { + const float bearing_ = float(i - 1) * bearingStep - mapBearing; + Point poc = ruler.destination(centerPoint, params.errorRadiusMeters, bearing_); + circle[i] = (Projection::project(LatLng(poc.y, poc.x), scale) - center); + } + radiusChanged = false; + } + + // Size in "map pixels" for a screen pixel + static float pixelSizeToWorldSizeH(const LatLng& pos, const TransformState& s) { + const double scale = s.getScale(); + ScreenCoordinate posScreen = latLngToScreenCoordinate(pos, s); + ScreenCoordinate posScreenLeftPx = posScreen; + posScreenLeftPx.x -= 1; + LatLng posLeftPx = screenCoordinateToLatLng(posScreenLeftPx, s); + Point posMerc = Projection::project(pos, scale); + Point posLeftPxMerc = Projection::project(posLeftPx, scale); + return vec2(posMerc - posLeftPxMerc).length(); + } + + static vec2 verticalDirectionMercator(const LatLng& pos, const TransformState& s) { + const double scale = s.getScale(); + ScreenCoordinate posScreen = latLngToScreenCoordinate(pos, s); + Point posMerc = Projection::project(pos, scale); + return verticalDirectionMercator(posScreen, posMerc, s); + } + + static vec2 verticalDirectionMercator(const ScreenCoordinate& pos, Point posMerc, const TransformState& s) { + const double scale = s.getScale(); + ScreenCoordinate screenDy = pos; + screenDy.y -= 1; + LatLng posDy = screenCoordinateToLatLng(screenDy, s); + Point posMercDy = Projection::project(posDy, scale); + return verticalDirectionMercator(posMerc, posMercDy); + } + + static vec2 verticalDirectionMercator(const Point& posMerc, const Point& posMercDy) { + Point verticalShiftMercator = posMercDy - posMerc; + vec2 res(verticalShiftMercator); + return res.normalized(); + } + + static Point hatShadowShiftVector(const LatLng& position, + const mbgl::style::LocationComponentRenderParameters& params) { + const TransformState& s = *params.state; + const double scale = s.getScale(); + ScreenCoordinate posScreen = latLngToScreenCoordinate(position, s); + posScreen.y = params.height - 1; // moving it to bottom + Point posMerc = Projection::project(screenCoordinateToLatLng(posScreen, s), scale); + vec2 verticalShiftAtPos = verticalDirectionMercator(posScreen, posMerc, s); + return Point(verticalShiftAtPos.x, verticalShiftAtPos.y); + } + + static vec2 directionAtPositionScreen(const LatLng& position, float bearing, const TransformState& s) { + const double scale = s.getScale(); + const vec2 rot = vec2(0.0, -1.0).rotated(-bearing); + Point posMerc = Projection::project(position, scale); + Point posMercDelta = posMerc + rot.point(); + ScreenCoordinate posScreen = latLngToScreenCoordinate(position, s); + ScreenCoordinate posScreenDelta = latLngToScreenCoordinate(Projection::unproject(posMercDelta, scale), s); + return vec2(posScreenDelta - posScreen).normalized(); + } + + void updatePuck(const mbgl::style::LocationComponentRenderParameters& params) { + if (params.useProperProjection) return updatePuckPerspective(params); + updatePuckNoPerspective(params); + } + + void updatePuckNoPerspective(const mbgl::style::LocationComponentRenderParameters& params) { + const TransformState& s = *params.state; + const mapbox::cheap_ruler::point centerPoint(params.puckPosition.longitude(), params.puckPosition.latitude()); + static constexpr ScreenCoordinate cornerOffsets[]{ + {-1.0, -1.0}, + {-1.0, 1.0}, + {1.0, 1.0}, + {1.0, -1.0}}; // Quads will be drawn as triangle fans. so bl, tl, tr, br + + vec4 centerProjected; + ScreenCoordinate centerScreen = s.latLngToScreenCoordinate(params.puckPosition, centerProjected); + if (centerProjected[3] < 0.0) { + // crd behind the camera. don't draw. + for (int i = 0; i < 4; ++i) shadowGeometry[i] = puckGeometry[i] = hatGeometry[i] = {0, 0}; + return; + } + + const float horizontalScaleFactor = + params.perspectiveCompensation + + util::clamp(pixelSizeToWorldSizeH(params.puckPosition, s), 0.8f, 100.1f) * + (1.0f - params.perspectiveCompensation); // Compensation factor for the perspective deformation + const double shadowRadius = + (params.puckShadowSizePx * M_SQRT2 * 0.5) / + horizontalScaleFactor; // Technically it's not the radius, but the half diagonal of the quad. + const double puckRadius = (params.puckSizePx * M_SQRT2 * 0.5) / horizontalScaleFactor; + const double hatRadius = (params.puckHatSizePx * M_SQRT2 * 0.5) / horizontalScaleFactor; + + centerScreen = latLngToScreenCoordinate(params.puckPosition, s); + // Workaround buggy wrap + if (centerScreen.x >= + params.width + std::max(std::max(puckRadius, hatRadius), shadowRadius)) { // one more hack. + centerScreen = + latLngToScreenCoordinate({params.puckPosition.latitude(), params.puckPosition.longitude() - 360.0}, s); + } // This isn't enough. It's still broken on the y axis when the map is 90 or 270 degrees rotated. + // A real fix is needed <-- Todo: check map.pixelForLatLng + + vec2 directionProjected = directionAtPositionScreen(params.puckPosition, params.puckBearing, s); + + // Perspectiveless transformation is rotate -> tilt -> shift by position in screen space. + matrix::identity(projectionPuck); + mat4 proj; + matrix::ortho(proj, 0, params.width, params.height, 0, -1000, 1000); + mat4 rot; + matrix::identity(rot); + matrix::rotate_z(rot, rot, directionProjected.bearing() * util::DEG2RAD); + + mat4 tilt; + matrix::identity(tilt); + matrix::rotate_x(tilt, tilt, params.pitch); + matrix::multiply(rot, tilt, rot); + + mat4 shift; + matrix::identity(shift); + matrix::translate(shift, shift, centerScreen.x, centerScreen.y, 0); + matrix::multiply(rot, shift, rot); + + matrix::multiply(projectionPuck, proj, rot); + + // calculate a decently looking hat shift + vec2 perspectiveShift = hatShadowShiftVector(params.puckPosition, params); // The perspective-projected vector + float bearingDiff = perspectiveShift.bearing(); + if (bearingDiff > 180.0) bearingDiff -= 360.0; + + vec2 upRotated = vec2(0.0, -1.0).rotated(directionProjected.bearing()); + upRotated = upRotated.rotated(bearingDiff * 0.50); // Modulate the amount of shift to not look too extreme + Point verticalShift = {upRotated.x, upRotated.y}; + centerScreen = {0, 0}; + for (unsigned long i = 0; i < 4; ++i) { + const Point cornerDirection = cornerOffsets[i]; + + Point shadowOffset = cornerDirection * shadowRadius; + Point puckOffset = cornerDirection * puckRadius; + Point hatOffset = cornerDirection * hatRadius; + + shadowGeometry[i] = + vec2(centerScreen - shadowOffset + + (verticalShift * (params.pitch * -params.puckLayersDisplacement / horizontalScaleFactor))); + puckGeometry[i] = vec2(centerScreen - puckOffset); + hatGeometry[i] = + vec2(centerScreen - hatOffset + + (verticalShift * (params.pitch * params.puckLayersDisplacement / horizontalScaleFactor))); + } + bearingChanged = false; + } + + void updatePuckPerspective(const mbgl::style::LocationComponentRenderParameters& params) { + const TransformState& s = *params.state; + projectionPuck = projectionCircle; // Duplicated as it might change, depending on what puck style is chosen. + const mapbox::cheap_ruler::point centerPoint(params.puckPosition.longitude(), params.puckPosition.latitude()); + static constexpr float bearings[]{ + 225.0f, 315.0f, 45.0f, 135.0f}; // Quads will be drawn as triangle fans. so bl, tl, tr, br +#ifndef M_SQRT2 + static constexpr const float M_SQRT2 = std::sqrt(2.0f); +#endif + // The puck has to stay square at all zoom levels. CheapRuler::destination does not guarantee this at low zoom + // levels, so the extent has to be produced in mercator space + const double tilt = s.getPitch(); + + // Point verticalShiftAtCenter { float(std::sin(util::DEG2RAD * util::wrap(-t.getBearing() * + // util::RAD2DEG, 0.0f, 360.0f) )), + // -float(std::cos(util::DEG2RAD * util::wrap(-t.getBearing() * + // util::RAD2DEG, 0.0f, 360.0f))) }; + // would be correct only in the vertical center of the map. As soon as position goes away from that line, + // the shift direction is skewed by the perspective projection. + // So the way to have a shift aligned to the screen vertical axis is to find this direction in screen space, and + // convert it back to map space. This would yield an always straight up shift. However, going further (= the + // opposite direction of where the lines are converging in the projection) might produce an even more realistic + // effect. But in this case, it empirically seems that the largest shift that look acceptable is what is + // obtained at the bottom of the window, avoiding the wider converging lines that pass by the edge of the screen + // going toward the top. + + Point verticalShift = hatShadowShiftVector(params.puckPosition, params); + const float horizontalScaleFactor = + (1.0f - params.perspectiveCompensation) + + util::clamp(pixelSizeToWorldSizeH(params.puckPosition, s), 0.8f, 100.1f) * + params.perspectiveCompensation; // Compensation factor for the perspective deformation + // ^ clamping this to 0.8 to avoid growing the puck too much close to the camera. + const double shadowRadius = + params.puckShadowSizePx * M_SQRT2 * 0.5 * + horizontalScaleFactor; // Technically it's not the radius, but the half diagonal of the quad. + const double puckRadius = params.puckSizePx * M_SQRT2 * 0.5 * horizontalScaleFactor; + const double hatRadius = params.puckHatSizePx * M_SQRT2 * 0.5 * horizontalScaleFactor; + + for (unsigned long i = 0; i < 4; ++i) { + const float b = util::wrap(params.puckBearing + bearings[i], 0.0f, 360.0f); + + const Point cornerDirection{float(std::sin(util::DEG2RAD * b)), + -float(std::cos(util::DEG2RAD * b))}; + + Point shadowOffset = cornerDirection * shadowRadius; + Point puckOffset = cornerDirection * puckRadius; + Point hatOffset = cornerDirection * hatRadius; + + shadowGeometry[i] = + vec2(shadowOffset + (verticalShift * (tilt * -params.puckLayersDisplacement * horizontalScaleFactor))); + puckGeometry[i] = vec2(puckOffset); + hatGeometry[i] = + vec2(hatOffset + (verticalShift * (tilt * params.puckLayersDisplacement * horizontalScaleFactor))); + } + + bearingChanged = false; + } + + void drawRadius(const mbgl::style::LocationComponentRenderParameters& params) { + if (!(params.errorRadiusMeters > 0.0) || + (params.errorRadiusColor.a == 0.0 && params.errorRadiusBorderColor.a == 0.0)) + return; + + simpleShader.bind(); + mbgl::gl::bindUniform(simpleShader.u_color, params.errorRadiusColor); + mbgl::gl::bindUniform(simpleShader.u_matrix, projectionCircle); + + circleBuffer.upload(circle); + MBGL_CHECK_ERROR(glEnableVertexAttribArray(simpleShader.a_pos)); + MBGL_CHECK_ERROR(glVertexAttribPointer(simpleShader.a_pos, 2, GL_FLOAT, GL_FALSE, 0, nullptr)); + + MBGL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_FAN, 0, circle.size())); + if (params.errorRadiusBorderColor.a > 0.0f) { + mbgl::gl::bindUniform(simpleShader.u_color, params.errorRadiusBorderColor); + MBGL_CHECK_ERROR(glDrawArrays(GL_LINE_STRIP, 1, circle.size() - 1)); // line width: 1 by default; + } + MBGL_CHECK_ERROR(glDisableVertexAttribArray(simpleShader.a_pos)); + circleBuffer.detach(); + simpleShader.detach(); + } + + void drawQuad(Buffer& buf, std::array& data, std::shared_ptr& texture) { + if (!texture || !texture->isValid()) return; + texturedShader.bind(); + texture->bind(0); + glUniform1i(texturedShader.u_image, 0); + mbgl::gl::bindUniform(texturedShader.u_matrix, projectionPuck); + + buf.bind(); + buf.upload(data); + MBGL_CHECK_ERROR(glEnableVertexAttribArray(texturedShader.a_pos)); + MBGL_CHECK_ERROR(glVertexAttribPointer(texturedShader.a_pos, 2, GL_FLOAT, GL_FALSE, 0, nullptr)); + + texCoordsBuffer.bind(); + MBGL_CHECK_ERROR(glEnableVertexAttribArray(texturedShader.a_texCoord)); + MBGL_CHECK_ERROR(glVertexAttribPointer(texturedShader.a_texCoord, 2, GL_FLOAT, GL_FALSE, 0, nullptr)); + + MBGL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_FAN, 0, 4)); + texture->detach(); // ToDo: optimize attach and detaches. + texCoordsBuffer.detach(); + texturedShader.detach(); + } + + void drawShadow() { drawQuad(shadowBuffer, shadowGeometry, texShadow); } + + void drawPuck() { drawQuad(puckBuffer, puckGeometry, texPuck); } + + void drawHat() { drawQuad(hatBuffer, hatGeometry, texPuckHat); } + + static ScreenCoordinate latLngToScreenCoordinate(const LatLng& p, const TransformState& s) { + ScreenCoordinate point = s.latLngToScreenCoordinate(p); + point.y = s.getSize().height - point.y; + return point; + } + + static LatLng screenCoordinateToLatLng(const ScreenCoordinate& p, + const TransformState& s, + LatLng::WrapMode wrapMode = LatLng::Wrapped) { + ScreenCoordinate flippedPoint = p; + flippedPoint.y = s.getSize().height - flippedPoint.y; + return s.screenCoordinateToLatLng(flippedPoint, wrapMode); + } + + void setTextureFromFile(const std::string& imagePath, + std::shared_ptr& tex, + const mbgl::style::LocationComponentRenderParameters& params) { + if (textures.find(imagePath) == textures.end()) { + std::shared_ptr tx = std::make_shared(); + if (!imagePath.empty() && params.imageManager) + tx->assign(&(params.imageManager->getImage(imagePath)->image)); + else + tx->assign(nullptr); + textures[imagePath] = tx; + } + tex = textures.at(imagePath); + } + + std::map> textures; + mapbox::cheap_ruler::CheapRuler ruler; + SimpleShader simpleShader; + TexturedShader texturedShader; + Buffer buffer; + Buffer circleBuffer; + Buffer shadowBuffer; + Buffer puckBuffer; + Buffer hatBuffer; + Buffer texCoordsBuffer; + std::shared_ptr texShadow; + std::shared_ptr texPuck; + std::shared_ptr texPuckHat; + + std::array circle; // 72 points + position + std::array shadowGeometry; + std::array puckGeometry; + std::array hatGeometry; + std::array texCoords; + mbgl::mat4 projectionCircle; + mbgl::mat4 projectionPuck; + + bool positionChanged = false; + bool radiusChanged = false; + bool bearingChanged = false; + mbgl::style::LocationComponentRenderParameters oldParams; +}; +} // namespace style +} // namespace mbgl + +namespace mbgl { +namespace style { + +LocationComponentLayer::LocationComponentLayer(const std::string& layerID) + : Layer(makeMutable(layerID, new PuckLayerHost())) {} +LocationComponentLayer::LocationComponentLayer(Immutable impl_) : Layer(std::move(impl_)) {} + +LocationComponentLayer::~LocationComponentLayer() = default; + +const LocationComponentLayer::Impl& LocationComponentLayer::impl() const { + return static_cast(*baseImpl); +} + +Mutable LocationComponentLayer::mutableImpl() const { + return makeMutable(impl()); +} + +std::unique_ptr LocationComponentLayer::cloneRef(const std::string& id_) const { + auto impl_ = mutableImpl(); + impl_->id = id_; + impl_->paint = LocationComponentPaintProperties::Transitionable(); + // impl_->paint = LocationComponentPaintProperties::PossiblyEvaluated(); + return std::make_unique(std::move(impl_)); +} + +using namespace conversion; + +// static +const LayerTypeInfo* LocationComponentLayer::Impl::staticTypeInfo() noexcept { + const static LayerTypeInfo typeInfo{"location-component", + LayerTypeInfo::Source::NotRequired, + LayerTypeInfo::Pass3D::NotRequired, + LayerTypeInfo::Layout::NotRequired, + LayerTypeInfo::FadingTiles::NotRequired, + LayerTypeInfo::CrossTileIndex::NotRequired, + LayerTypeInfo::TileKind::NotRequired}; + return &typeInfo; +} + +// Layout properties + +void LocationComponentLayer::setLocation(const LatLng& crd) { + std::array data; + data[0] = crd.latitude(); + data[1] = crd.longitude(); + data[2] = 0; + setLocation(data); +} + +PropertyValue LocationComponentLayer::getDefaultAccuracyRadius() { + return AccuracyRadius::defaultValue(); +} + +const PropertyValue& LocationComponentLayer::getAccuracyRadius() const { + return impl().layout.get(); +} + +void LocationComponentLayer::setAccuracyRadius(const PropertyValue& value) { + if (value == getAccuracyRadius()) return; + auto impl_ = mutableImpl(); + impl_->layout.get() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} +PropertyValue LocationComponentLayer::getDefaultBearing() { + return Bearing::defaultValue(); +} + +const PropertyValue& LocationComponentLayer::getBearing() const { + return impl().layout.get(); +} + +void LocationComponentLayer::setBearing(const PropertyValue& value) { + if (value == getBearing()) return; + auto impl_ = mutableImpl(); + impl_->layout.get() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} +PropertyValue LocationComponentLayer::getDefaultBearingImage() { + return BearingImage::defaultValue(); +} + +const PropertyValue& LocationComponentLayer::getBearingImage() const { + return impl().layout.get(); +} + +void LocationComponentLayer::setBearingImage(const PropertyValue& value) { + if (value == getBearingImage()) return; + auto impl_ = mutableImpl(); + impl_->layout.get() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} +PropertyValue LocationComponentLayer::getDefaultBearingImageSize() { + return BearingImageSize::defaultValue(); +} + +const PropertyValue& LocationComponentLayer::getBearingImageSize() const { + return impl().layout.get(); +} + +void LocationComponentLayer::setBearingImageSize(const PropertyValue& value) { + if (value == getBearingImageSize()) return; + auto impl_ = mutableImpl(); + impl_->layout.get() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} +PropertyValue LocationComponentLayer::getDefaultImageTiltDisplacement() { + return ImageTiltDisplacement::defaultValue(); +} + +const PropertyValue& LocationComponentLayer::getImageTiltDisplacement() const { + return impl().layout.get(); +} + +void LocationComponentLayer::setImageTiltDisplacement(const PropertyValue& value) { + if (value == getImageTiltDisplacement()) return; + auto impl_ = mutableImpl(); + impl_->layout.get() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} +PropertyValue> LocationComponentLayer::getDefaultLocation() { + return Location::defaultValue(); +} + +const PropertyValue>& LocationComponentLayer::getLocation() const { + return impl().layout.get(); +} + +void LocationComponentLayer::setLocation(const PropertyValue>& value) { + if (value == getLocation()) return; + auto impl_ = mutableImpl(); + impl_->layout.get() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} +PropertyValue LocationComponentLayer::getDefaultPerspectiveCompensation() { + return PerspectiveCompensation::defaultValue(); +} + +const PropertyValue& LocationComponentLayer::getPerspectiveCompensation() const { + return impl().layout.get(); +} + +void LocationComponentLayer::setPerspectiveCompensation(const PropertyValue& value) { + if (value == getPerspectiveCompensation()) return; + auto impl_ = mutableImpl(); + impl_->layout.get() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} +PropertyValue LocationComponentLayer::getDefaultShadowImage() { + return ShadowImage::defaultValue(); +} + +const PropertyValue& LocationComponentLayer::getShadowImage() const { + return impl().layout.get(); +} + +void LocationComponentLayer::setShadowImage(const PropertyValue& value) { + if (value == getShadowImage()) return; + auto impl_ = mutableImpl(); + impl_->layout.get() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} +PropertyValue LocationComponentLayer::getDefaultShadowImageSize() { + return ShadowImageSize::defaultValue(); +} + +const PropertyValue& LocationComponentLayer::getShadowImageSize() const { + return impl().layout.get(); +} + +void LocationComponentLayer::setShadowImageSize(const PropertyValue& value) { + if (value == getShadowImageSize()) return; + auto impl_ = mutableImpl(); + impl_->layout.get() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} +PropertyValue LocationComponentLayer::getDefaultTopImage() { + return TopImage::defaultValue(); +} + +const PropertyValue& LocationComponentLayer::getTopImage() const { + return impl().layout.get(); +} + +void LocationComponentLayer::setTopImage(const PropertyValue& value) { + if (value == getTopImage()) return; + auto impl_ = mutableImpl(); + impl_->layout.get() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} +PropertyValue LocationComponentLayer::getDefaultTopImageSize() { + return TopImageSize::defaultValue(); +} + +const PropertyValue& LocationComponentLayer::getTopImageSize() const { + return impl().layout.get(); +} + +void LocationComponentLayer::setTopImageSize(const PropertyValue& value) { + if (value == getTopImageSize()) return; + auto impl_ = mutableImpl(); + impl_->layout.get() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} + +// Paint properties + +PropertyValue LocationComponentLayer::getDefaultAccuracyRadiusBorderColor() { + return {Color::white()}; +} + +const PropertyValue& LocationComponentLayer::getAccuracyRadiusBorderColor() const { + return impl().paint.template get().value; +} + +void LocationComponentLayer::setAccuracyRadiusBorderColor(const PropertyValue& value) { + if (value == getAccuracyRadiusBorderColor()) return; + auto impl_ = mutableImpl(); + impl_->paint.template get().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} + +void LocationComponentLayer::setAccuracyRadiusBorderColorTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get().options = options; + baseImpl = std::move(impl_); +} + +TransitionOptions LocationComponentLayer::getAccuracyRadiusBorderColorTransition() const { + return impl().paint.template get().options; +} + +PropertyValue LocationComponentLayer::getDefaultAccuracyRadiusColor() { + return {Color::white()}; +} + +const PropertyValue& LocationComponentLayer::getAccuracyRadiusColor() const { + return impl().paint.template get().value; +} + +void LocationComponentLayer::setAccuracyRadiusColor(const PropertyValue& value) { + if (value == getAccuracyRadiusColor()) return; + auto impl_ = mutableImpl(); + impl_->paint.template get().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} + +void LocationComponentLayer::setAccuracyRadiusColorTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get().options = options; + baseImpl = std::move(impl_); +} + +TransitionOptions LocationComponentLayer::getAccuracyRadiusColorTransition() const { + return impl().paint.template get().options; +} + +using namespace conversion; + +namespace { + +constexpr uint8_t kPaintPropertyCount = 4u; + +enum class Property : uint8_t { + AccuracyRadiusBorderColor, + AccuracyRadiusColor, + AccuracyRadiusBorderColorTransition, + AccuracyRadiusColorTransition, + AccuracyRadius = kPaintPropertyCount, + Bearing, + BearingImage, + BearingImageSize, + ImageTiltDisplacement, + Location, + PerspectiveCompensation, + ShadowImage, + ShadowImageSize, + TopImage, + TopImageSize, +}; + +template +constexpr uint8_t toUint8(T t) noexcept { + return uint8_t(mbgl::underlying_type(t)); +} + +MAPBOX_ETERNAL_CONSTEXPR const auto layerProperties = mapbox::eternal::hash_map( + {{"accuracy-radius-border-color", toUint8(Property::AccuracyRadiusBorderColor)}, + {"accuracy-radius-color", toUint8(Property::AccuracyRadiusColor)}, + {"accuracy-radius-border-color-transition", toUint8(Property::AccuracyRadiusBorderColorTransition)}, + {"accuracy-radius-color-transition", toUint8(Property::AccuracyRadiusColorTransition)}, + {"accuracy-radius", toUint8(Property::AccuracyRadius)}, + {"bearing", toUint8(Property::Bearing)}, + {"bearing-image", toUint8(Property::BearingImage)}, + {"bearing-image-size", toUint8(Property::BearingImageSize)}, + {"image-tilt-displacement", toUint8(Property::ImageTiltDisplacement)}, + {"location", toUint8(Property::Location)}, + {"perspective-compensation", toUint8(Property::PerspectiveCompensation)}, + {"shadow-image", toUint8(Property::ShadowImage)}, + {"shadow-image-size", toUint8(Property::ShadowImageSize)}, + {"top-image", toUint8(Property::TopImage)}, + {"top-image-size", toUint8(Property::TopImageSize)}}); + +StyleProperty getLayerProperty(const LocationComponentLayer& layer, Property property) { + switch (property) { + case Property::AccuracyRadiusBorderColor: + return makeStyleProperty(layer.getAccuracyRadiusBorderColor()); + case Property::AccuracyRadiusColor: + return makeStyleProperty(layer.getAccuracyRadiusColor()); + case Property::AccuracyRadiusBorderColorTransition: + return makeStyleProperty(layer.getAccuracyRadiusBorderColorTransition()); + case Property::AccuracyRadiusColorTransition: + return makeStyleProperty(layer.getAccuracyRadiusColorTransition()); + case Property::AccuracyRadius: + return makeStyleProperty(layer.getAccuracyRadius()); + case Property::Bearing: + return makeStyleProperty(layer.getBearing()); + case Property::BearingImage: + return makeStyleProperty(layer.getBearingImage()); + case Property::BearingImageSize: + return makeStyleProperty(layer.getBearingImageSize()); + case Property::ImageTiltDisplacement: + return makeStyleProperty(layer.getImageTiltDisplacement()); + case Property::Location: + return makeStyleProperty(layer.getLocation()); + case Property::PerspectiveCompensation: + return makeStyleProperty(layer.getPerspectiveCompensation()); + case Property::ShadowImage: + return makeStyleProperty(layer.getShadowImage()); + case Property::ShadowImageSize: + return makeStyleProperty(layer.getShadowImageSize()); + case Property::TopImage: + return makeStyleProperty(layer.getTopImage()); + case Property::TopImageSize: + return makeStyleProperty(layer.getTopImageSize()); + } + return {}; +} + +StyleProperty getLayerProperty(const LocationComponentLayer& layer, const std::string& name) { + const auto it = layerProperties.find(name.c_str()); + if (it == layerProperties.end()) { + return {}; + } + return getLayerProperty(layer, static_cast(it->second)); +} + +} // namespace + +Value LocationComponentLayer::serialize() const { + auto result = Layer::serialize(); + assert(result.getObject()); + for (const auto& property : layerProperties) { + auto styleProperty = getLayerProperty(*this, static_cast(property.second)); + if (styleProperty.getKind() == StyleProperty::Kind::Undefined) continue; + serializeProperty(result, styleProperty, property.first.c_str(), property.second < kPaintPropertyCount); + } + return result; +} + +optional LocationComponentLayer::setPropertyInternal(const std::string& name, const Convertible& value) { + const auto it = layerProperties.find(name.c_str()); + if (it == layerProperties.end()) return Error{"layer doesn't support this property"}; + + auto property = static_cast(it->second); + + if (property == Property::AccuracyRadiusBorderColor || property == Property::AccuracyRadiusColor) { + Error error; + const auto& typedValue = convert>(value, error, false, false); + if (!typedValue) { + return error; + } + + if (property == Property::AccuracyRadiusBorderColor) { + setAccuracyRadiusBorderColor(*typedValue); + return nullopt; + } + + if (property == Property::AccuracyRadiusColor) { + setAccuracyRadiusColor(*typedValue); + return nullopt; + } + } + if (property == Property::AccuracyRadius || property == Property::Bearing || + property == Property::BearingImageSize || property == Property::ImageTiltDisplacement || + property == Property::PerspectiveCompensation || property == Property::ShadowImageSize || + property == Property::TopImageSize) { + Error error; + const auto& typedValue = convert>(value, error, false, false); + if (!typedValue) { + return error; + } + + if (property == Property::AccuracyRadius) { + setAccuracyRadius(*typedValue); + return nullopt; + } + + if (property == Property::Bearing) { + setBearing(*typedValue); + return nullopt; + } + + if (property == Property::BearingImageSize) { + setBearingImageSize(*typedValue); + return nullopt; + } + + if (property == Property::ImageTiltDisplacement) { + setImageTiltDisplacement(*typedValue); + return nullopt; + } + + if (property == Property::PerspectiveCompensation) { + setPerspectiveCompensation(*typedValue); + return nullopt; + } + + if (property == Property::ShadowImageSize) { + setShadowImageSize(*typedValue); + return nullopt; + } + + if (property == Property::TopImageSize) { + setTopImageSize(*typedValue); + return nullopt; + } + } + if (property == Property::BearingImage || property == Property::ShadowImage || property == Property::TopImage) { + Error error; + const auto& typedValue = convert>(value, error, false, false); + if (!typedValue) { + return error; + } + + if (property == Property::BearingImage) { + setBearingImage(*typedValue); + return nullopt; + } + + if (property == Property::ShadowImage) { + setShadowImage(*typedValue); + return nullopt; + } + + if (property == Property::TopImage) { + setTopImage(*typedValue); + return nullopt; + } + } + if (property == Property::Location) { + Error error; + const auto& typedValue = convert>>(value, error, false, false); + if (!typedValue) { + return error; + } + + setLocation(*typedValue); + return nullopt; + } + + Error error; + optional transition = convert(value, error); + if (!transition) { + return error; + } + + if (property == Property::AccuracyRadiusBorderColorTransition) { + setAccuracyRadiusBorderColorTransition(*transition); + return nullopt; + } + + if (property == Property::AccuracyRadiusColorTransition) { + setAccuracyRadiusColorTransition(*transition); + return nullopt; + } + + return Error{"layer doesn't support this property"}; +} + +StyleProperty LocationComponentLayer::getProperty(const std::string& name) const { + return getLayerProperty(*this, name); +} + +Mutable LocationComponentLayer::mutableBaseImpl() const { + return staticMutableCast(mutableImpl()); +} +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/gl/location_component_layer_factory.cpp b/src/mbgl/gl/location_component_layer_factory.cpp new file mode 100644 index 00000000000..6eee961ba3c --- /dev/null +++ b/src/mbgl/gl/location_component_layer_factory.cpp @@ -0,0 +1,25 @@ +#include +#include +#include +#include + +namespace mbgl { + +const style::LayerTypeInfo* LocationComponentLayerFactory::getTypeInfo() const noexcept { + return style::LocationComponentLayer::Impl::staticTypeInfo(); +} + +std::unique_ptr LocationComponentLayerFactory::createLayer( + const std::string& id, const style::conversion::Convertible& value) noexcept { + (void)value; + return std::unique_ptr(new style::LocationComponentLayer(id)); // Need the map here. Or as a property? +} + +std::unique_ptr LocationComponentLayerFactory::createRenderLayer( + Immutable impl) noexcept { + assert(impl->getTypeInfo() == getTypeInfo()); + return std::make_unique( + staticImmutableCast(impl)); +} + +} // namespace mbgl diff --git a/src/mbgl/gl/location_component_layer_impl.cpp b/src/mbgl/gl/location_component_layer_impl.cpp new file mode 100644 index 00000000000..9ea266d09ed --- /dev/null +++ b/src/mbgl/gl/location_component_layer_impl.cpp @@ -0,0 +1,19 @@ +#include +#include + +namespace mbgl { +namespace style { + +LocationComponentLayer::Impl::Impl(const std::string& id_, LocationComponentLayerHost* host_) + : Layer::Impl(id_, std::string()) { + host = std::shared_ptr(host_); +} + +bool LocationComponentLayer::Impl::hasLayoutDifference(const Layer::Impl&) const { + return false; +} + +void LocationComponentLayer::Impl::stringifyLayout(rapidjson::Writer&) const {} + +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/gl/location_component_layer_impl.hpp b/src/mbgl/gl/location_component_layer_impl.hpp new file mode 100644 index 00000000000..7d5999c5bdf --- /dev/null +++ b/src/mbgl/gl/location_component_layer_impl.hpp @@ -0,0 +1,81 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mbgl { + +class TransformState; + +namespace style { + +struct LocationComponentRenderParameters { + LocationComponentRenderParameters() = default; + LocationComponentRenderParameters(const TransformParameters &tp) : state(&tp.state) {} + LocationComponentRenderParameters(const LocationComponentRenderParameters &o) = default; + LocationComponentRenderParameters &operator=(const LocationComponentRenderParameters &o) = default; + + double width; + double height; + double latitude; + double longitude; + double zoom; + double bearing; + double pitch; + double fieldOfView; + std::array projectionMatrix; + const TransformState *state = nullptr; + ImageManager *imageManager = nullptr; + // some testing defaults, for before it gets updated via props + double puckBearing = 0.0; + LatLng puckPosition = {0, 0}; + double errorRadiusMeters; + mbgl::Color errorRadiusColor; + mbgl::Color errorRadiusBorderColor; + int puckSizePx = 0; + int puckHatSizePx = 0; + int puckShadowSizePx = 0; + float puckLayersDisplacement = 0; + float perspectiveCompensation = 0; + bool useProperProjection = true; + std::string puckImagePath; + std::string puckShadowImagePath; + std::string puckHatImagePath; +}; + +// This is a clone of CustomLayerHost. For documentation, refer to that +class LocationComponentLayerHost { +public: + virtual ~LocationComponentLayerHost() = default; + virtual void initialize() = 0; + virtual void render(const LocationComponentRenderParameters &) = 0; + virtual void contextLost() = 0; + virtual void deinitialize() = 0; +}; + +class LocationComponentLayer::Impl : public Layer::Impl { +public: + Impl(const std::string &id, LocationComponentLayerHost *host); + + bool hasLayoutDifference(const Layer::Impl &) const override; + void stringifyLayout(rapidjson::Writer &) const override; + + std::shared_ptr host; + + LocationComponentLayoutProperties::Unevaluated layout; + // LocationComponentPaintProperties::PossiblyEvaluated paint; + LocationComponentPaintProperties::Transitionable paint; + DECLARE_LAYER_TYPE_INFO; +}; + +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/gl/location_component_layer_properties.cpp b/src/mbgl/gl/location_component_layer_properties.cpp new file mode 100644 index 00000000000..5e2f70c9857 --- /dev/null +++ b/src/mbgl/gl/location_component_layer_properties.cpp @@ -0,0 +1,34 @@ +// clang-format off + +// This file is generated. Edit scripts/generate-style-code.js, then run `make style-code`. + +#include +#include + +namespace mbgl { +namespace style { + +LocationComponentLayerProperties::LocationComponentLayerProperties( + Immutable impl_) + : LayerProperties(std::move(impl_)) {} + +LocationComponentLayerProperties::LocationComponentLayerProperties( + Immutable impl_, + LocationComponentPaintProperties::PossiblyEvaluated evaluated_) + : LayerProperties(std::move(impl_)), + evaluated(std::move(evaluated_)) {} + +LocationComponentLayerProperties::~LocationComponentLayerProperties() = default; + +unsigned long LocationComponentLayerProperties::constantsMask() const { + return evaluated.constantsMask(); +} + +const LocationComponentLayer::Impl& LocationComponentLayerProperties::layerImpl() const { + return static_cast(*baseImpl); +} + +} // namespace style +} // namespace mbgl + +// clang-format on diff --git a/src/mbgl/gl/location_component_layer_properties.hpp b/src/mbgl/gl/location_component_layer_properties.hpp new file mode 100644 index 00000000000..3f8140a6c62 --- /dev/null +++ b/src/mbgl/gl/location_component_layer_properties.hpp @@ -0,0 +1,119 @@ +// clang-format off + +// This file is generated. Edit scripts/generate-style-code.js, then run `make style-code`. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mbgl { +namespace style { + +struct AccuracyRadius : LayoutProperty { + static constexpr const char *name() { return "accuracy-radius"; } + static float defaultValue() { return 0; } +}; + +struct Bearing : LayoutProperty { + static constexpr const char *name() { return "bearing"; } + static float defaultValue() { return 0; } +}; + +struct BearingImage : LayoutProperty { + static constexpr const char *name() { return "bearing-image"; } + static expression::Image defaultValue() { return {}; } +}; + +struct BearingImageSize : LayoutProperty { + static constexpr const char *name() { return "bearing-image-size"; } + static float defaultValue() { return 1; } +}; + +struct ImageTiltDisplacement : LayoutProperty { + static constexpr const char *name() { return "image-tilt-displacement"; } + static float defaultValue() { return 0; } +}; + +struct Location : LayoutProperty> { + static constexpr const char *name() { return "location"; } + static std::array defaultValue() { return {{0, 0, 0}}; } +}; + +struct PerspectiveCompensation : LayoutProperty { + static constexpr const char *name() { return "perspective-compensation"; } + static float defaultValue() { return 0.85; } +}; + +struct ShadowImage : LayoutProperty { + static constexpr const char *name() { return "shadow-image"; } + static expression::Image defaultValue() { return {}; } +}; + +struct ShadowImageSize : LayoutProperty { + static constexpr const char *name() { return "shadow-image-size"; } + static float defaultValue() { return 1; } +}; + +struct TopImage : LayoutProperty { + static constexpr const char *name() { return "top-image"; } + static expression::Image defaultValue() { return {}; } +}; + +struct TopImageSize : LayoutProperty { + static constexpr const char *name() { return "top-image-size"; } + static float defaultValue() { return 1; } +}; + +struct AccuracyRadiusBorderColor : PaintProperty { + static Color defaultValue() { return Color::white(); } +}; + +struct AccuracyRadiusColor : PaintProperty { + static Color defaultValue() { return Color::white(); } +}; + +class LocationComponentLayoutProperties : public Properties< + AccuracyRadius, + Bearing, + BearingImage, + BearingImageSize, + ImageTiltDisplacement, + Location, + PerspectiveCompensation, + ShadowImage, + ShadowImageSize, + TopImage, + TopImageSize +> {}; + +class LocationComponentPaintProperties : public Properties< + AccuracyRadiusBorderColor, + AccuracyRadiusColor +> {}; + +class LocationComponentLayerProperties final : public LayerProperties { +public: + explicit LocationComponentLayerProperties(Immutable); + LocationComponentLayerProperties( + Immutable, + LocationComponentPaintProperties::PossiblyEvaluated); + ~LocationComponentLayerProperties() override; + + unsigned long constantsMask() const override; + + const LocationComponentLayer::Impl& layerImpl() const; + // Data members. + LocationComponentPaintProperties::PossiblyEvaluated evaluated; +}; + +} // namespace style +} // namespace mbgl + +// clang-format on diff --git a/src/mbgl/gl/render_location_component_layer.cpp b/src/mbgl/gl/render_location_component_layer.cpp new file mode 100644 index 00000000000..c9b58aefed1 --- /dev/null +++ b/src/mbgl/gl/render_location_component_layer.cpp @@ -0,0 +1,133 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mbgl { + +using namespace style; + +namespace { + +inline const LocationComponentLayer::Impl& impl(const Immutable& impl) { + assert(impl->getTypeInfo() == LocationComponentLayer::Impl::staticTypeInfo()); + return static_cast(*impl); +} + +} // namespace + +RenderLocationComponentLayer::RenderLocationComponentLayer(Immutable _impl) + : RenderLayer(makeMutable(std::move(_impl))), + host(impl(baseImpl).host), + unevaluated(impl(baseImpl).paint.untransitioned()) { + assert(gfx::BackendScope::exists()); + MBGL_CHECK_ERROR(host->initialize()); +} + +RenderLocationComponentLayer::~RenderLocationComponentLayer() { + assert(gfx::BackendScope::exists()); + if (contextDestroyed) { + host->contextLost(); + } else { + MBGL_CHECK_ERROR(host->deinitialize()); + } +} + +void RenderLocationComponentLayer::evaluate(const PropertyEvaluationParameters& parameters) { + passes = RenderPass::Translucent; + auto properties = makeMutable( + staticImmutableCast(baseImpl), unevaluated.evaluate(parameters)); + const auto& evaluated = properties->evaluated; + auto& layout = impl(baseImpl).layout; + + properties->renderPasses = mbgl::underlying_type(passes); + + { + params.puckBearing = layout.get().asConstant(); + params.errorRadiusMeters = layout.get().asConstant(); + params.errorRadiusColor = evaluated.get(); + params.errorRadiusBorderColor = evaluated.get(); + params.puckSizePx = layout.get().asConstant(); + params.puckHatSizePx = layout.get().asConstant(); + params.puckShadowSizePx = layout.get().asConstant(); + params.puckLayersDisplacement = layout.get().asConstant(); + params.perspectiveCompensation = layout.get().asConstant(); + params.useProperProjection = true; + + const std::array pos = layout.get().asConstant(); + params.puckPosition = LatLng{pos[0], pos[1]}; + params.puckImagePath = layout.get().asConstant().id(); + params.puckShadowImagePath = layout.get().asConstant().id(); + params.puckHatImagePath = layout.get().asConstant().id(); + } + evaluatedProperties = std::move(properties); +} + +bool RenderLocationComponentLayer::hasTransition() const { + return false; +} +bool RenderLocationComponentLayer::hasCrossfade() const { + return false; +} + +void RenderLocationComponentLayer::markContextDestroyed() { + contextDestroyed = true; +} + +void RenderLocationComponentLayer::prepare(const LayerPrepareParameters& p) { + params.imageManager = &p.imageManager; +} + +void RenderLocationComponentLayer::render(PaintParameters& paintParameters) { + if (host != impl(baseImpl).host) { + // If the context changed, deinitialize the previous one before initializing the new one. + if (host && !contextDestroyed) { + MBGL_CHECK_ERROR(host->deinitialize()); + } + host = impl(baseImpl).host; + MBGL_CHECK_ERROR(host->initialize()); + } + + auto& glContext = static_cast(paintParameters.context); + const TransformState& state = paintParameters.state; + + // Reset GL state to a known state so the CustomLayer always has a clean slate. + glContext.bindVertexArray = 0; + glContext.setDepthMode(paintParameters.depthModeForSublayer(0, gfx::DepthMaskType::ReadOnly)); + glContext.setStencilMode(gfx::StencilMode::disabled()); + glContext.setColorMode(paintParameters.colorModeForRenderPass()); + glContext.setCullFaceMode(gfx::CullFaceMode::disabled()); + + LocationComponentRenderParameters parameters = params; + parameters.state = &paintParameters.transformParams.state; + + parameters.width = state.getSize().width; + parameters.height = state.getSize().height; + parameters.latitude = state.getLatLng().latitude(); + parameters.longitude = state.getLatLng().longitude(); + parameters.zoom = state.getZoom(); + parameters.bearing = -state.getBearing() * util::RAD2DEG; + parameters.pitch = state.getPitch(); + parameters.fieldOfView = state.getFieldOfView(); + mat4 projMatrix; + state.getProjMatrix(projMatrix); + parameters.projectionMatrix = projMatrix; + + MBGL_CHECK_ERROR(host->render(parameters)); + + // Reset the view back to our original one, just in case the CustomLayer changed + // the viewport or Framebuffer. + paintParameters.backend.getDefaultRenderable().getResource().bind(); + glContext.setDirtyState(); +} + +} // namespace mbgl diff --git a/src/mbgl/gl/render_location_component_layer.hpp b/src/mbgl/gl/render_location_component_layer.hpp new file mode 100644 index 00000000000..80b0b94353d --- /dev/null +++ b/src/mbgl/gl/render_location_component_layer.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace mbgl { + +class RenderLocationComponentLayer final : public RenderLayer { +public: + explicit RenderLocationComponentLayer(Immutable); + ~RenderLocationComponentLayer() override; + +private: + void transition(const TransitionParameters&) override {} + void evaluate(const PropertyEvaluationParameters&) override; + bool hasTransition() const override; + bool hasCrossfade() const override; + void markContextDestroyed() override; + void prepare(const LayerPrepareParameters&) override; + + void render(PaintParameters&) override; + + bool contextDestroyed = false; + std::shared_ptr host; + // style::LocationComponentPaintProperties::PossiblyEvaluated possiblyEvaluated; + style::LocationComponentPaintProperties::Unevaluated unevaluated; + style::LocationComponentRenderParameters params; +}; + +} // namespace mbgl diff --git a/src/mbgl/style/conversion/position.cpp b/src/mbgl/style/conversion/position.cpp index df6c868ee82..f3d36b69231 100644 --- a/src/mbgl/style/conversion/position.cpp +++ b/src/mbgl/style/conversion/position.cpp @@ -17,7 +17,6 @@ optional Converter::operator()(const Convertible& value, Err return Position(*spherical); } - } // namespace conversion } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/layers/layer.cpp.ejs b/src/mbgl/style/layers/layer.cpp.ejs index bfdcd6e054d..61adfc7b395 100644 --- a/src/mbgl/style/layers/layer.cpp.ejs +++ b/src/mbgl/style/layers/layer.cpp.ejs @@ -78,6 +78,7 @@ layerCapabilities['heatmap'] = defaults.require('Source') .set('TileKind', 'Geometry') .finalize(); layerCapabilities['raster'] = defaults.require('Source').set('TileKind', 'Raster').finalize(); +layerCapabilities['location-component'] = defaults.finalize(); // commented to not regenerate this one // Splits lines that are over 120 characters at the firts occurance of '='. const split120Line = line => {