diff --git a/CMakeLists.txt b/CMakeLists.txt index 4357493cef1..496dfd2f13b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -808,6 +808,17 @@ 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..b33ef7814ae --- /dev/null +++ b/include/mbgl/gl/location_component_layer.hpp @@ -0,0 +1,100 @@ +#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 +#include + +using namespace mbgl; +using namespace mbgl::style; + +namespace mbgl { +namespace style { + +class LocationComponentLayer : public Layer { +public: + LocationComponentLayer(const std::string& id); + ~LocationComponentLayer() final; + + // Dynamic properties + optional setPropertyInternal(const std::string& name, + const conversion::Convertible& value) final; + StyleProperty getProperty(const std::string&) const final; + + class Impl; + const Impl& impl() const; + + Mutable mutableImpl() const; + LocationComponentLayer(Immutable); + std::unique_ptr cloneRef(const std::string& id) const final; + + LocationComponentLayer(const LocationComponentLayer&) = delete; + + Value serialize() const final; + Mutable mutableBaseImpl() const final; + + // Paint properties + static PropertyValue getDefaultAccuracyRadius(); + const PropertyValue getAccuracyRadius() const; + void setAccuracyRadius(const PropertyValue&); + + static PropertyValue getDefaultAccuracyRadiusBorderColor(); + const PropertyValue getAccuracyRadiusBorderColor() const; + void setAccuracyRadiusBorderColor(const PropertyValue&); + + static PropertyValue getDefaultAccuracyRadiusColor(); + const PropertyValue getAccuracyRadiusColor() const; + void setAccuracyRadiusColor(const PropertyValue&); + + static PropertyValue getDefaultTopImage(); + const PropertyValue getTopImage() const; + void setTopImage(const PropertyValue&); + + static PropertyValue getDefaultTopImageSize(); + const PropertyValue getTopImageSize() const; + void setTopImageSize(const PropertyValue&); + + static PropertyValue getDefaultBearing(); + const PropertyValue getBearing() const; + void setBearing(const PropertyValue&); + + static PropertyValue getDefaultBearingImage(); + const PropertyValue getBearingImage() const; + void setBearingImage(const PropertyValue&); + + static PropertyValue getDefaultBearingImageSize(); + const PropertyValue getBearingImageSize() const; + void setBearingImageSize(const PropertyValue&); + + static PropertyValue getDefaultLocation(); + const PropertyValue getLocation() const; + void setLocation(const PropertyValue&); + void setCoordinate(const LatLng &crd); + + static PropertyValue getDefaultShadowImage(); + const PropertyValue getShadowImage() const; + void setShadowImage(const PropertyValue&); + + static PropertyValue getDefaultShadowImageSize(); + const PropertyValue getShadowImageSize() const; + void setShadowImageSize(const PropertyValue&); +}; +} // 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..51fcff0e87a --- /dev/null +++ b/include/mbgl/gl/location_component_layer_factory.hpp @@ -0,0 +1,14 @@ +#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/include/mbgl/style/position.hpp b/include/mbgl/style/position.hpp index 3be8d1c55e0..1ce150f6475 100644 --- a/include/mbgl/style/position.hpp +++ b/include/mbgl/style/position.hpp @@ -46,12 +46,12 @@ class Position { } private: - float radial; - float azimuthal; - float polar; - float x; - float y; - float z; + float radial = 0; + float azimuthal = 0; + float polar = 0; + float x = 0; + float y = 0; + float z = 0; void calculateCartesian() { // We abstract "north"/"up" (compass-wise) to be 0° when really this is 90° (π/2): we 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..21df2e57d4e 100644 --- a/platform/glfw/CMakeLists.txt +++ b/platform/glfw/CMakeLists.txt @@ -13,6 +13,13 @@ 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..9507b5c647f 100644 --- a/platform/glfw/glfw_view.cpp +++ b/platform/glfw/glfw_view.cpp @@ -28,6 +28,9 @@ #include #include +// glif this one +#include + #if MBGL_USE_GLES2 #define GLFW_INCLUDE_ES2 #endif // MBGL_USE_GLES2 @@ -41,6 +44,8 @@ #include #include +static const std::string mbglPuckAssetsPath{MAPBOX_PUCK_ASSETS_PATH}; + class SnapshotObserver final : public mbgl::MapSnapshotterObserver { public: ~SnapshotObserver() override = default; @@ -459,6 +464,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 +697,11 @@ void GLFWView::onScroll(GLFWwindow *window, double /*xOffset*/, double yOffset) } view->map->scaleBy(scale, mbgl::ScreenCoordinate { view->lastX, view->lastY }); + + if (view->puck) { + mbgl::LatLng mapCenter = view->map->getCameraOptions().center.value(); + view->puck->setCoordinate(mapCenter); + } } void GLFWView::onWindowResize(GLFWwindow *window, int width, int height) { @@ -756,6 +769,11 @@ void GLFWView::onMouseMove(GLFWwindow *window, double x, double y) { view->lastX = x; view->lastY = y; + if (view->puck) { + mbgl::LatLng mapCenter = view->map->getCameraOptions().center.value(); + view->puck->setCoordinate(mapCenter); + } + auto &style = view->map->getStyle(); if (style.getLayer("state-fills")) { auto screenCoordinate = mbgl::ScreenCoordinate{view->lastX, view->lastY}; @@ -879,6 +897,8 @@ void GLFWView::setWindowTitle(const std::string& title) { } void GLFWView::onDidFinishLoadingStyle() { + puck = nullptr; + if (show3DExtrusions) { toggle3DExtrusions(show3DExtrusions); } @@ -933,3 +953,40 @@ void GLFWView::toggleCustomSource() { mbgl::style::VisibilityType::None : mbgl::style::VisibilityType::Visible); } } + +//static const int numPucks = 1; //00; +void GLFWView::toggleLocationComponentLayer() +{ + if (map->getStyle().getLayer("puck") == nullptr) { + auto puckLayer = std::make_unique("puck"); + puckLayer->setBearingImage(expression::Image(mbglPuckAssetsPath+"puck.png")); + puckLayer->setBearingImageSize(72); + puckLayer->setBearing(0); + puckLayer->setCoordinate(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->setTopImage(expression::Image(mbglPuckAssetsPath+"puck_hat.png")); + puckLayer->setTopImageSize(24); + puckLayer->setShadowImage(expression::Image(mbglPuckAssetsPath+"puck_shadow.png")); + puckLayer->setShadowImageSize(96); + + puck = puckLayer.get(); + map->getStyle().addLayer(std::move(puckLayer)); + } else { + bool visible = bool(int(puck->getVisibility())); + puck->setVisibility(mbgl::style::VisibilityType(!visible)); + } +} + +using Nanoseconds = std::chrono::nanoseconds; + +void GLFWView::onWillStartRenderingFrame() +{ + uint64_t ns = mbgl::Clock::now().time_since_epoch().count(); + const float bearing = float(ns % 2000000000) / 2000000000.0 * 360.0; // testing only + + if (puck) { + puck->setBearing(bearing); + } +} diff --git a/platform/glfw/glfw_view.hpp b/platform/glfw/glfw_view.hpp index 21b54f87a9c..6f65bd76730 100644 --- a/platform/glfw/glfw_view.hpp +++ b/platform/glfw/glfw_view.hpp @@ -6,6 +6,7 @@ #include #include #include +#include struct GLFWwindow; class GLFWBackend; @@ -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,5 @@ class GLFWView : public mbgl::MapObserver { std::unique_ptr snapshotter; std::unique_ptr snapshotterObserver; mbgl::ResourceOptions mapResourceOptions; + mbgl::style::LocationComponentLayer *puck = nullptr; }; diff --git a/scripts/style-spec.js b/scripts/style-spec.js index 8a9c9d4144c..b4af9080752 100644 --- a/scripts/style-spec.js +++ b/scripts/style-spec.js @@ -1 +1,140 @@ -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"] = {}; +referenceSpec["paint_location-component"] = { + "bearing": { + "type": "number", + "default": "0", + "property-type": "data-constant", + "transition": false, + "expression": { + "interpolated": false, + "parameters": [ + "zoom" + ] + } + }, + "position": { + "type": "array", + "default": [ + 0.0, + 0.0, + 0.0 + ], + "length": 3, + "value": "number", + "property-type": "data-constant", + "transition": false, + "expression": { + "interpolated": false, + "parameters": [ + "zoom" + ] + } + }, + "accuracy-radius": { + "type": "number", + "default": "0", + "property-type": "data-constant", + "transition": true, + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + } + }, + "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 + }, + "background-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 + }, + "background-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" + ] + } + } +}; + +var spec = module.exports = referenceSpec diff --git a/src/mbgl/gl/location_component_layer.cpp b/src/mbgl/gl/location_component_layer.cpp new file mode 100644 index 00000000000..76d20038d8d --- /dev/null +++ b/src/mbgl/gl/location_component_layer.cpp @@ -0,0 +1,1269 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mbgl { +namespace style { +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(std::shared_ptr img) { + if (img.get() == uploaded) + return; + + imageDirty = true; + image = img; + } + + void upload() { + if (!imageDirty) + return; + imageDirty = false; + initialize(); + uploaded = image.get(); + 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, uploaded->size.width, uploaded->size.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, uploaded->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(); + image.reset(); + + } + void initialize() { + if (texId != 0) + return; + MBGL_CHECK_ERROR(glGenTextures(1, &texId)); + } + void bind(int textureUnit = -1) { + initialize(); + if (!uploaded && !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 || uploaded ; + } + GLuint texId = 0; + const mbgl::PremultipliedImage *uploaded = nullptr; + std::shared_ptr image; + 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); + if (params.puckShadowImagePath != oldParams.puckShadowImagePath) setTextureFromFile(params.puckShadowImagePath, texShadow); + if (params.puckHatImagePath != oldParams.puckHatImagePath) setTextureFromFile(params.puckHatImagePath, texPuckHat); + + 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); + else + return 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 = map.pixelForLatLng(position); + 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) { + if (textures.find(imagePath) == textures.end()) { + std::shared_ptr tx = std::make_shared(); + if (!imagePath.empty()) { + std::shared_ptr ima; + if (imagePath.rfind("file://", 0) == 0) { + ima = std::make_shared( + mbgl::decodeImage(mbgl::util::read_file(imagePath.substr(7))) // ToDo: trycatch or check that file exists + ); + } else { // Or load from ImageManager? + ima = std::make_shared( + mbgl::decodeImage(mbgl::util::read_file(imagePath)) + ); + } + tx->assign(ima); + } + 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 { + +namespace { +const LayerTypeInfo typeInfoPuck{"", + LayerTypeInfo::Source::NotRequired, + LayerTypeInfo::Pass3D::NotRequired, + LayerTypeInfo::Layout::NotRequired, + LayerTypeInfo::FadingTiles::NotRequired, + LayerTypeInfo::CrossTileIndex::NotRequired, + LayerTypeInfo::TileKind::NotRequired}; +} // namespace + +//LocationComponentLayer::LocationComponentLayer(const std::string& layerID, const mbgl::Map &map) +// : Layer(makeMutable(layerID, new LocationComponentLayerHost(map))) { +//} + +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; + +//optional LocationComponentLayer::setPropertyInternal(const std::string&, +// const conversion::Convertible&) { +// return Error { "layer doesn't support this property" }; +//} + +//StyleProperty LocationComponentLayer::getProperty(const std::string&) const { +// return {}; +//} + +Mutable LocationComponentLayer::mutableBaseImpl() const { + return staticMutableCast(mutableImpl()); +} + +// static +const LayerTypeInfo* LocationComponentLayer::Impl::staticTypeInfo() noexcept { + return &typeInfoPuck; +} + +// Paint properties + +PropertyValue LocationComponentLayer::getDefaultAccuracyRadius() { + return {0}; +} + +const PropertyValue LocationComponentLayer::getAccuracyRadius() const { + return impl().paint.template get(); +} + +void LocationComponentLayer::setAccuracyRadius(const PropertyValue& value) { + if (value == getAccuracyRadius()) + return; + auto impl_ = mutableImpl(); + impl_->paint.template get() = value.asConstant(); + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} + +PropertyValue LocationComponentLayer::getDefaultAccuracyRadiusBorderColor() { + return {Color::white()}; +} + +const PropertyValue LocationComponentLayer::getAccuracyRadiusBorderColor() const { + return impl().paint.template get(); +} + +void LocationComponentLayer::setAccuracyRadiusBorderColor(const PropertyValue& value) { + if (value == getAccuracyRadiusBorderColor()) + return; + auto impl_ = mutableImpl(); + impl_->paint.template get() = value.asConstant(); + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} + +PropertyValue LocationComponentLayer::getDefaultAccuracyRadiusColor() { + return {Color::white()}; +} + +const PropertyValue LocationComponentLayer::getAccuracyRadiusColor() const { + return impl().paint.template get(); +} + +void LocationComponentLayer::setAccuracyRadiusColor(const PropertyValue& value) { + if (value == getAccuracyRadiusColor()) + return; + auto impl_ = mutableImpl(); + impl_->paint.template get() = value.asConstant(); + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} + +PropertyValue LocationComponentLayer::getDefaultTopImage() { + return {{}}; +} + +const PropertyValue LocationComponentLayer::getTopImage() const { + return impl().paint.template get(); +} + +void LocationComponentLayer::setTopImage(const PropertyValue& value) { + if (value == getTopImage()) + return; + auto impl_ = mutableImpl(); + impl_->paint.template get() = value.asConstant(); +// impl_->images.get() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} + +PropertyValue LocationComponentLayer::getDefaultTopImageSize() { + return {1}; +} + +const PropertyValue LocationComponentLayer::getTopImageSize() const { + return impl().paint.template get(); +} + +void LocationComponentLayer::setTopImageSize(const PropertyValue& value) { + if (value == getTopImageSize()) + return; + auto impl_ = mutableImpl(); + impl_->paint.template get() = value.asConstant(); + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} + +PropertyValue LocationComponentLayer::getDefaultBearing() { + return {0}; +} + +const PropertyValue LocationComponentLayer::getBearing() const { + return impl().paint.template get(); +} + +void LocationComponentLayer::setBearing(const PropertyValue& value) { + if (value == getBearing()) + return; + auto impl_ = mutableImpl(); + impl_->paint.template get() = value.asConstant(); + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} + +PropertyValue LocationComponentLayer::getDefaultBearingImage() { + return {{}}; +} + +const PropertyValue LocationComponentLayer::getBearingImage() const { + return impl().paint.template get(); +} + +void LocationComponentLayer::setBearingImage(const PropertyValue& value) { + if (value == getBearingImage()) + return; + printf("setBearingImage %s\n", value.asConstant().id().c_str()); fflush(stdout); + auto impl_ = mutableImpl(); + impl_->paint.template get() = value.asConstant(); + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} + +PropertyValue LocationComponentLayer::getDefaultBearingImageSize() { + return {1}; +} + +const PropertyValue LocationComponentLayer::getBearingImageSize() const { + return impl().paint.template get(); +} + +void LocationComponentLayer::setBearingImageSize(const PropertyValue& value) { + if (value == getBearingImageSize()) + return; + auto impl_ = mutableImpl(); + impl_->paint.template get() = value.asConstant(); + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} + +PropertyValue LocationComponentLayer::getDefaultLocation() { + return Position(); +} + +const PropertyValue LocationComponentLayer::getLocation() const { + return impl().paint.template get(); +} + +void LocationComponentLayer::setLocation(const PropertyValue& value) { + if (value == getLocation()) + return; + auto impl_ = mutableImpl(); + impl_->paint.template get() = value.asConstant(); + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} + +void LocationComponentLayer::setCoordinate(const LatLng &crd) +{ + std::array data; + data[0] = crd.latitude(); + data[1] = crd.longitude(); + data[2] = 0; + Position p(data); + setLocation(p); +} + +PropertyValue LocationComponentLayer::getDefaultShadowImage() { + return {{}}; +} + +const PropertyValue LocationComponentLayer::getShadowImage() const { + return impl().paint.template get(); +} + +void LocationComponentLayer::setShadowImage(const PropertyValue& value) { + if (value == getShadowImage()) + return; + auto impl_ = mutableImpl(); + impl_->paint.template get() = value.asConstant(); + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} + +PropertyValue LocationComponentLayer::getDefaultShadowImageSize() { + return {1}; +} + +const PropertyValue LocationComponentLayer::getShadowImageSize() const { + return impl().paint.template get(); +} + +void LocationComponentLayer::setShadowImageSize(const PropertyValue& value) { + if (value == getShadowImageSize()) + return; + auto impl_ = mutableImpl(); + impl_->paint.template get() = value.asConstant(); + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} + +using namespace conversion; + +namespace { + +constexpr uint8_t kPaintPropertyCount = 22u; + +enum class Property : uint8_t { + AccuracyRadius, + AccuracyRadiusBorderColor, + AccuracyRadiusColor, + TopImage, + TopImageSize, + Bearing, + BearingImage, + BearingImageSize, + Location, + ShadowImage, + ShadowImageSize +// AccuracyRadiusTransition, +// AccuracyRadiusBorderColorTransition, +// AccuracyRadiusColorTransition, +// TopImageSizeTransition, +// BearingTransition, +// BearingImageSizeTransition, +// LocationTransition, +// ShadowImageSizeTransition, +}; + +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", toUint8(Property::AccuracyRadius)}, + {"accuracy-radius-border-color", toUint8(Property::AccuracyRadiusBorderColor)}, + {"accuracy-radius-color", toUint8(Property::AccuracyRadiusColor)}, + {"background-image", toUint8(Property::TopImage)}, + {"background-image-size", toUint8(Property::TopImageSize)}, + {"bearing", toUint8(Property::Bearing)}, + {"bearing-image", toUint8(Property::BearingImage)}, + {"bearing-image-size", toUint8(Property::BearingImageSize)}, + {"location", toUint8(Property::Location)}, + {"shadow-image", toUint8(Property::ShadowImage)}, + {"shadow-image-size", toUint8(Property::ShadowImageSize)} +// {"accuracy-radius-transition", toUint8(Property::AccuracyRadiusTransition)}, +// {"accuracy-radius-border-color-transition", toUint8(Property::AccuracyRadiusBorderColorTransition)}, +// {"accuracy-radius-color-transition", toUint8(Property::AccuracyRadiusColorTransition)}, +// {"background-image-size-transition", toUint8(Property::TopImageSizeTransition)}, +// {"bearing-transition", toUint8(Property::BearingTransition)}, +// {"bearing-image-size-transition", toUint8(Property::BearingImageSizeTransition)}, +// {"location-transition", toUint8(Property::LocationTransition)}, +// {"shadow-image-size-transition", toUint8(Property::ShadowImageSizeTransition)} + }); + +StyleProperty getLayerProperty(const LocationComponentLayer& layer, Property property) { + switch (property) { + case Property::AccuracyRadius: + return makeStyleProperty(layer.getAccuracyRadius()); + case Property::AccuracyRadiusBorderColor: + return makeStyleProperty(layer.getAccuracyRadiusBorderColor()); + case Property::AccuracyRadiusColor: + return makeStyleProperty(layer.getAccuracyRadiusColor()); + case Property::TopImage: + return makeStyleProperty(layer.getTopImage()); + case Property::TopImageSize: + return makeStyleProperty(layer.getTopImageSize()); + case Property::Bearing: + return makeStyleProperty(layer.getBearing()); + case Property::BearingImage: + return makeStyleProperty(layer.getBearingImage()); + case Property::BearingImageSize: + return makeStyleProperty(layer.getBearingImageSize()); + case Property::Location: + return makeStyleProperty(layer.getLocation()); + case Property::ShadowImage: + return makeStyleProperty(layer.getShadowImage()); + case Property::ShadowImageSize: + return makeStyleProperty(layer.getShadowImageSize()); +// case Property::AccuracyRadiusTransition: +// return makeStyleProperty(layer.getAccuracyRadiusTransition()); +// case Property::AccuracyRadiusBorderColorTransition: +// return makeStyleProperty(layer.getAccuracyRadiusBorderColorTransition()); +// case Property::AccuracyRadiusColorTransition: +// return makeStyleProperty(layer.getAccuracyRadiusColorTransition()); +// case Property::TopImageSizeTransition: +// return makeStyleProperty(layer.getTopImageSizeTransition()); +// case Property::BearingTransition: +// return makeStyleProperty(layer.getBearingTransition()); +// case Property::BearingImageSizeTransition: +// return makeStyleProperty(layer.getBearingImageSizeTransition()); +// case Property::LocationTransition: +// return makeStyleProperty(layer.getLocationTransition()); +// case Property::ShadowImageSizeTransition: +// return makeStyleProperty(layer.getShadowImageSizeTransition()); + } + 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::AccuracyRadius || property == Property::TopImageSize || + property == Property::Bearing || property == Property::BearingImageSize || + property == Property::ShadowImageSize) { + 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::TopImageSize) { + setTopImageSize(*typedValue); + return nullopt; + } + + if (property == Property::Bearing) { + setBearing(*typedValue); + return nullopt; + } + + if (property == Property::BearingImageSize) { + setBearingImageSize(*typedValue); + return nullopt; + } + + if (property == Property::ShadowImageSize) { + setShadowImageSize(*typedValue); + return nullopt; + } + } + 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::TopImage || property == Property::BearingImage || + property == Property::ShadowImage) { + Error error; + const auto& typedValue = convert>(value, error, false, false); + if (!typedValue) { + return error; + } + + if (property == Property::TopImage) { + setTopImage(*typedValue); + return nullopt; + } + + if (property == Property::BearingImage) { + setBearingImage(*typedValue); + return nullopt; + } + + if (property == Property::ShadowImage) { + setShadowImage(*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::AccuracyRadiusTransition) { +// setAccuracyRadiusTransition(*transition); +// return nullopt; +// } + +// if (property == Property::AccuracyRadiusBorderColorTransition) { +// setAccuracyRadiusBorderColorTransition(*transition); +// return nullopt; +// } + +// if (property == Property::AccuracyRadiusColorTransition) { +// setAccuracyRadiusColorTransition(*transition); +// return nullopt; +// } + +// if (property == Property::TopImageSizeTransition) { +// setTopImageSizeTransition(*transition); +// return nullopt; +// } + +// if (property == Property::BearingTransition) { +// setBearingTransition(*transition); +// return nullopt; +// } + +// if (property == Property::BearingImageSizeTransition) { +// setBearingImageSizeTransition(*transition); +// return nullopt; +// } + +// if (property == Property::LocationTransition) { +// setLocationTransition(*transition); +// return nullopt; +// } + +// if (property == Property::ShadowImageSizeTransition) { +// setShadowImageSizeTransition(*transition); +// return nullopt; +// } + + return Error{"layer doesn't support this property"}; +} + +StyleProperty LocationComponentLayer::getProperty(const std::string& name) const { + return getLayerProperty(*this, name); +} + +} // 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..a81dd9a3ff7 --- /dev/null +++ b/src/mbgl/gl/location_component_layer_factory.cpp @@ -0,0 +1,22 @@ +#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..534b84dd45b --- /dev/null +++ b/src/mbgl/gl/location_component_layer_impl.cpp @@ -0,0 +1,21 @@ +#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..f836d96171a --- /dev/null +++ b/src/mbgl/gl/location_component_layer_impl.hpp @@ -0,0 +1,78 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mbgl { + +class TransformState; + +namespace style { + +struct LocationComponentRenderParameters { + LocationComponentRenderParameters() {}; + 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; + // 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; + + LocationComponentPaintProperties::PossiblyEvaluated 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..037f4c0b204 --- /dev/null +++ b/src/mbgl/gl/location_component_layer_properties.cpp @@ -0,0 +1,32 @@ +// clang-format off +#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..25af56a3b54 --- /dev/null +++ b/src/mbgl/gl/location_component_layer_properties.hpp @@ -0,0 +1,97 @@ +// clang-format off +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mbgl { +namespace style { + +struct AccuracyRadius : PaintProperty { + static float defaultValue() { return 0; } +}; + +struct AccuracyRadiusBorderColor : PaintProperty { + static Color defaultValue() { return Color::white(); } +}; + +struct AccuracyRadiusColor : PaintProperty { + static Color defaultValue() { return Color::white(); } +}; + +// CrossFadedPaintProperty or DataDrivenLayoutProperty +struct TopImage : PaintProperty { + static expression::Image defaultValue() { return {}; } +}; + +struct TopImageSize : PaintProperty { + static float defaultValue() { return 1; } +}; + +struct Bearing : PaintProperty { + static float defaultValue() { return 0; } +}; + +struct BearingImage : PaintProperty { + static expression::Image defaultValue() { return {}; } +}; + +struct BearingImageSize : PaintProperty { + static float defaultValue() { return 1; } +}; + +struct Location : PaintProperty { // ToDo: find the way to remove this one and use doubles + static Position defaultValue() { return Position(); } +}; + +struct ShadowImage : PaintProperty { + static expression::Image defaultValue() { return {}; } +}; + +struct ShadowImageSize : PaintProperty { + static float defaultValue() { return 1; } +}; + +class LocationComponentPaintProperties : public Properties< + AccuracyRadius, + AccuracyRadiusBorderColor, + AccuracyRadiusColor, + TopImage, + TopImageSize, + Bearing, + BearingImage, + BearingImageSize, + Location, + ShadowImage, + ShadowImageSize, + TopImage, + BearingImage, + ShadowImage +> {}; + +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..785c26913f2 --- /dev/null +++ b/src/mbgl/gl/render_location_component_layer.cpp @@ -0,0 +1,132 @@ +#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), + possiblyEvaluated(impl(baseImpl).paint) { + 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; +} + +bool RenderLocationComponentLayer::hasTransition() const { + return false; +} +bool RenderLocationComponentLayer::hasCrossfade() const { + return false; +} + +void RenderLocationComponentLayer::markContextDestroyed() { + contextDestroyed = true; +} + +void RenderLocationComponentLayer::prepare(const LayerPrepareParameters&) { +} + +void RenderLocationComponentLayer::render(PaintParameters& paintParameters) { + printf("RenderLocationComponentLayer::render\n"); + 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()); + } + + possiblyEvaluated = impl(baseImpl).paint; + { // ToDo: find a better place for where to pull these properties, instead of at every render call? + auto properties = makeMutable( + staticImmutableCast(baseImpl), + possiblyEvaluated); + + properties->renderPasses = mbgl::underlying_type(passes); + evaluatedProperties = std::move(properties); + + params.puckBearing = possiblyEvaluated.get(); + const std::array pos = possiblyEvaluated.get().getSpherical(); + params.puckPosition = LatLng{pos[0], pos[1]}; + params.errorRadiusMeters = possiblyEvaluated.get(); + params.errorRadiusColor = possiblyEvaluated.get(); + params.errorRadiusBorderColor = possiblyEvaluated.get(); + params.puckSizePx = possiblyEvaluated.get(); + params.puckHatSizePx = possiblyEvaluated.get(); + params.puckShadowSizePx = possiblyEvaluated.get(); + params.puckLayersDisplacement = 8.0f; // missing in params atm + params.perspectiveCompensation = 0.88f; // missing too.. + params.useProperProjection = true; + params.puckImagePath = possiblyEvaluated.get().id(); + params.puckShadowImagePath = possiblyEvaluated.get().id(); + params.puckHatImagePath = possiblyEvaluated.get().id(); + } + + 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..c4dd0b9a830 --- /dev/null +++ b/src/mbgl/gl/render_location_component_layer.hpp @@ -0,0 +1,32 @@ +#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; + LocationComponentRenderParameters params; +}; + +} // namespace mbgl diff --git a/src/mbgl/style/layers/layer.cpp.ejs b/src/mbgl/style/layers/layer.cpp.ejs index bfdcd6e054d..60dc97f3445 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 => {