summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmake/core-files.cmake25
-rw-r--r--cmake/test-files.cmake2
-rw-r--r--include/mbgl/map/map.hpp5
-rw-r--r--include/mbgl/style/conversion/constant.hpp49
-rw-r--r--include/mbgl/style/conversion/layer.hpp3
-rw-r--r--include/mbgl/style/conversion/light.hpp122
-rw-r--r--include/mbgl/style/conversion/position.hpp29
-rw-r--r--include/mbgl/style/layer.hpp2
-rw-r--r--include/mbgl/style/light.hpp52
-rw-r--r--include/mbgl/style/position.hpp68
-rw-r--r--include/mbgl/style/types.hpp5
-rw-r--r--include/mbgl/util/indexed_tuple.hpp (renamed from src/mbgl/util/indexed_tuple.hpp)0
-rw-r--r--include/mbgl/util/interpolate.hpp12
-rw-r--r--include/mbgl/util/type_list.hpp (renamed from src/mbgl/util/type_list.hpp)0
m---------mapbox-gl-js0
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillExtrusionLayer.java339
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java21
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java164
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/BackgroundLayerTest.java41
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillExtrusionLayerTest.java760
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml11
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionActivity.java135
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_fill_extrusion_layer.xml17
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml2
-rw-r--r--platform/android/config.cmake2
-rw-r--r--platform/android/scripts/generate-style-code.js3
-rw-r--r--platform/android/src/style/layers/fill_extrusion_layer.cpp200
-rw-r--r--platform/android/src/style/layers/fill_extrusion_layer.hpp62
-rw-r--r--platform/android/src/style/layers/layer.cpp4
-rw-r--r--platform/android/src/style/layers/layers.cpp8
-rw-r--r--platform/darwin/docs/guides/For Style Authors.md.ejs2
-rw-r--r--platform/darwin/scripts/generate-style-code.js5
-rw-r--r--platform/darwin/scripts/style-spec-cocoa-conventions-v8.json4
-rw-r--r--platform/darwin/scripts/style-spec-overrides-v8.json14
-rw-r--r--platform/darwin/src/MGLFillExtrusionStyleLayer.h364
-rw-r--r--platform/darwin/src/MGLFillExtrusionStyleLayer.mm335
-rw-r--r--platform/darwin/src/MGLStyle.mm4
-rw-r--r--platform/darwin/src/MGLStyleLayer.h.ejs2
-rw-r--r--platform/darwin/test/MGLDocumentationExampleTests.swift16
-rw-r--r--platform/darwin/test/MGLFillExtrusionStyleLayerTests.mm445
-rw-r--r--platform/darwin/test/MGLStyleLayerTests.mm.ejs2
-rw-r--r--platform/glfw/glfw_view.cpp10
-rw-r--r--platform/glfw/main.cpp2
-rw-r--r--platform/ios/CHANGELOG.md1
-rw-r--r--platform/ios/docs/guides/For Style Authors.md8
-rw-r--r--platform/ios/ios.xcodeproj/project.pbxproj16
-rw-r--r--platform/ios/jazzy.yml1
-rw-r--r--platform/ios/src/Mapbox.h1
m---------platform/ios/vendor/SMCalloutView0
-rw-r--r--platform/macos/CHANGELOG.md1
-rw-r--r--platform/macos/docs/guides/For Style Authors.md8
-rw-r--r--platform/macos/jazzy.yml1
-rw-r--r--platform/macos/macos.xcodeproj/project.pbxproj12
-rw-r--r--platform/macos/src/Mapbox.h1
-rwxr-xr-xscripts/generate-shaders.js3
-rw-r--r--scripts/style-code.js4
-rw-r--r--src/mbgl/gl/context.cpp12
-rw-r--r--src/mbgl/gl/context.hpp6
-rw-r--r--src/mbgl/gl/types.hpp5
-rw-r--r--src/mbgl/map/map.cpp17
-rw-r--r--src/mbgl/map/transform_state.cpp9
-rw-r--r--src/mbgl/map/transform_state.hpp2
-rw-r--r--src/mbgl/programs/attributes.hpp2
-rw-r--r--src/mbgl/programs/extrusion_texture_program.cpp7
-rw-r--r--src/mbgl/programs/extrusion_texture_program.hpp37
-rw-r--r--src/mbgl/programs/fill_extrusion_program.cpp81
-rw-r--r--src/mbgl/programs/fill_extrusion_program.hpp129
-rw-r--r--src/mbgl/programs/fill_program.hpp9
-rw-r--r--src/mbgl/programs/programs.hpp8
-rw-r--r--src/mbgl/programs/uniforms.hpp7
-rw-r--r--src/mbgl/renderer/circle_bucket.hpp1
-rw-r--r--src/mbgl/renderer/fill_extrusion_bucket.cpp177
-rw-r--r--src/mbgl/renderer/fill_extrusion_bucket.hpp38
-rw-r--r--src/mbgl/renderer/painter.cpp78
-rw-r--r--src/mbgl/renderer/painter.hpp11
-rw-r--r--src/mbgl/renderer/painter_background.cpp4
-rw-r--r--src/mbgl/renderer/painter_clipping.cpp2
-rw-r--r--src/mbgl/renderer/painter_fill_extrusion.cpp87
-rw-r--r--src/mbgl/renderer/painter_raster.cpp2
-rw-r--r--src/mbgl/renderer/render_fill_extrusion_layer.cpp41
-rw-r--r--src/mbgl/renderer/render_fill_extrusion_layer.hpp9
-rw-r--r--src/mbgl/renderer/render_item.hpp8
-rw-r--r--src/mbgl/renderer/render_tile.cpp48
-rw-r--r--src/mbgl/renderer/render_tile.hpp14
-rw-r--r--src/mbgl/shaders/extrusion_texture.cpp39
-rw-r--r--src/mbgl/shaders/extrusion_texture.hpp16
-rw-r--r--src/mbgl/shaders/fill_extrusion.cpp98
-rw-r--r--src/mbgl/shaders/fill_extrusion.hpp16
-rw-r--r--src/mbgl/shaders/fill_extrusion_pattern.cpp111
-rw-r--r--src/mbgl/shaders/fill_extrusion_pattern.hpp16
-rw-r--r--src/mbgl/shaders/preludes.cpp5
-rw-r--r--src/mbgl/style/layers/fill_extrusion_layer_impl.hpp2
-rw-r--r--src/mbgl/style/light_impl.hpp74
-rw-r--r--src/mbgl/style/paint_property.hpp75
-rw-r--r--src/mbgl/style/parser.cpp16
-rw-r--r--src/mbgl/style/parser.hpp4
-rw-r--r--src/mbgl/style/source_impl.cpp8
-rw-r--r--src/mbgl/style/source_impl.hpp1
-rw-r--r--src/mbgl/style/style.cpp22
-rw-r--r--src/mbgl/style/style.hpp5
-rw-r--r--src/mbgl/style/transitioning_property.hpp77
-rw-r--r--src/mbgl/style/types.cpp5
-rw-r--r--src/mbgl/util/mat3.cpp16
-rw-r--r--src/mbgl/util/mat3.hpp6
-rw-r--r--src/mbgl/util/offscreen_texture.cpp21
-rw-r--r--src/mbgl/util/offscreen_texture.hpp1
-rw-r--r--test/fixtures/style_parser/line-translate.info.json2
-rw-r--r--test/style/conversion/light.test.cpp101
-rw-r--r--test/style/paint_property.test.cpp39
-rw-r--r--test/util/position.test.cpp49
-rw-r--r--test/util/tile_cover.test.cpp5
111 files changed, 4853 insertions, 233 deletions
diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake
index 602cf90dad..d281872102 100644
--- a/cmake/core-files.cmake
+++ b/cmake/core-files.cmake
@@ -139,6 +139,10 @@ set(MBGL_CORE_FILES
src/mbgl/programs/collision_box_program.cpp
src/mbgl/programs/collision_box_program.hpp
src/mbgl/programs/debug_program.hpp
+ src/mbgl/programs/extrusion_texture_program.cpp
+ src/mbgl/programs/extrusion_texture_program.hpp
+ src/mbgl/programs/fill_extrusion_program.cpp
+ src/mbgl/programs/fill_extrusion_program.hpp
src/mbgl/programs/fill_program.cpp
src/mbgl/programs/fill_program.hpp
src/mbgl/programs/line_program.cpp
@@ -162,6 +166,8 @@ set(MBGL_CORE_FILES
src/mbgl/renderer/debug_bucket.hpp
src/mbgl/renderer/fill_bucket.cpp
src/mbgl/renderer/fill_bucket.hpp
+ src/mbgl/renderer/fill_extrusion_bucket.cpp
+ src/mbgl/renderer/fill_extrusion_bucket.hpp
src/mbgl/renderer/frame_history.cpp
src/mbgl/renderer/frame_history.hpp
src/mbgl/renderer/group_by_layout.cpp
@@ -176,6 +182,7 @@ set(MBGL_CORE_FILES
src/mbgl/renderer/painter_clipping.cpp
src/mbgl/renderer/painter_debug.cpp
src/mbgl/renderer/painter_fill.cpp
+ src/mbgl/renderer/painter_fill_extrusion.cpp
src/mbgl/renderer/painter_line.cpp
src/mbgl/renderer/painter_raster.cpp
src/mbgl/renderer/painter_symbol.cpp
@@ -213,8 +220,14 @@ set(MBGL_CORE_FILES
src/mbgl/shaders/collision_box.hpp
src/mbgl/shaders/debug.cpp
src/mbgl/shaders/debug.hpp
+ src/mbgl/shaders/extrusion_texture.cpp
+ src/mbgl/shaders/extrusion_texture.hpp
src/mbgl/shaders/fill.cpp
src/mbgl/shaders/fill.hpp
+ src/mbgl/shaders/fill_extrusion.cpp
+ src/mbgl/shaders/fill_extrusion.hpp
+ src/mbgl/shaders/fill_extrusion_pattern.cpp
+ src/mbgl/shaders/fill_extrusion_pattern.hpp
src/mbgl/shaders/fill_outline.cpp
src/mbgl/shaders/fill_outline.hpp
src/mbgl/shaders/fill_outline_pattern.cpp
@@ -270,6 +283,8 @@ set(MBGL_CORE_FILES
include/mbgl/style/image.hpp
include/mbgl/style/layer.hpp
include/mbgl/style/layer_type.hpp
+ include/mbgl/style/light.hpp
+ include/mbgl/style/position.hpp
include/mbgl/style/property_value.hpp
include/mbgl/style/query.hpp
include/mbgl/style/source.hpp
@@ -288,6 +303,7 @@ set(MBGL_CORE_FILES
src/mbgl/style/layer_impl.hpp
src/mbgl/style/layer_observer.hpp
src/mbgl/style/layout_property.hpp
+ src/mbgl/style/light_impl.hpp
src/mbgl/style/observer.hpp
src/mbgl/style/paint_property.hpp
src/mbgl/style/paint_property_binder.hpp
@@ -304,10 +320,9 @@ set(MBGL_CORE_FILES
src/mbgl/style/source_observer.hpp
src/mbgl/style/style.cpp
src/mbgl/style/style.hpp
- src/mbgl/style/style.hpp
- src/mbgl/style/style.hpp
src/mbgl/style/tile_source_impl.cpp
src/mbgl/style/tile_source_impl.hpp
+ src/mbgl/style/transitioning_property.hpp
src/mbgl/style/types.cpp
src/mbgl/style/update_batch.hpp
src/mbgl/style/update_parameters.hpp
@@ -320,7 +335,9 @@ set(MBGL_CORE_FILES
include/mbgl/style/conversion/geojson.hpp
include/mbgl/style/conversion/geojson_options.hpp
include/mbgl/style/conversion/layer.hpp
+ include/mbgl/style/conversion/light.hpp
include/mbgl/style/conversion/make_property_setters.hpp
+ include/mbgl/style/conversion/position.hpp
include/mbgl/style/conversion/property_setter.hpp
include/mbgl/style/conversion/property_value.hpp
include/mbgl/style/conversion/source.hpp
@@ -474,6 +491,7 @@ set(MBGL_CORE_FILES
include/mbgl/util/geometry.hpp
include/mbgl/util/ignore.hpp
include/mbgl/util/image.hpp
+ include/mbgl/util/indexed_tuple.hpp
include/mbgl/util/interpolate.hpp
include/mbgl/util/logging.hpp
include/mbgl/util/noncopyable.hpp
@@ -487,6 +505,7 @@ set(MBGL_CORE_FILES
include/mbgl/util/tileset.hpp
include/mbgl/util/timer.hpp
include/mbgl/util/traits.hpp
+ include/mbgl/util/type_list.hpp
include/mbgl/util/unitbezier.hpp
include/mbgl/util/util.hpp
include/mbgl/util/variant.hpp
@@ -514,7 +533,6 @@ set(MBGL_CORE_FILES
src/mbgl/util/http_timeout.hpp
src/mbgl/util/i18n.cpp
src/mbgl/util/i18n.hpp
- src/mbgl/util/indexed_tuple.hpp
src/mbgl/util/interpolate.cpp
src/mbgl/util/intersection_tests.cpp
src/mbgl/util/intersection_tests.hpp
@@ -548,7 +566,6 @@ set(MBGL_CORE_FILES
src/mbgl/util/tile_cover.cpp
src/mbgl/util/tile_cover.hpp
src/mbgl/util/token.hpp
- src/mbgl/util/type_list.hpp
src/mbgl/util/url.cpp
src/mbgl/util/url.hpp
src/mbgl/util/utf.hpp
diff --git a/cmake/test-files.cmake b/cmake/test-files.cmake
index 9012ae9616..51dedb0052 100644
--- a/cmake/test-files.cmake
+++ b/cmake/test-files.cmake
@@ -82,6 +82,7 @@ set(MBGL_TEST_FILES
test/style/conversion/function.test.cpp
test/style/conversion/geojson_options.test.cpp
test/style/conversion/layer.test.cpp
+ test/style/conversion/light.test.cpp
test/style/conversion/stringify.test.cpp
# style
@@ -125,6 +126,7 @@ set(MBGL_TEST_FILES
test/util/merge_lines.test.cpp
test/util/number_conversions.test.cpp
test/util/offscreen_texture.test.cpp
+ test/util/position.test.cpp
test/util/projection.test.cpp
test/util/run_loop.test.cpp
test/util/text_conversions.test.cpp
diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp
index 0e3cee4e70..84ea3104d8 100644
--- a/include/mbgl/map/map.hpp
+++ b/include/mbgl/map/map.hpp
@@ -30,6 +30,7 @@ namespace style {
class Image;
class Source;
class Layer;
+class Light;
} // namespace style
class Map : private util::noncopyable {
@@ -179,6 +180,10 @@ public:
void removeImage(const std::string&);
const style::Image* getImage(const std::string&);
+ // Light
+ void setLight(std::unique_ptr<style::Light>);
+ style::Light* getLight();
+
// Defaults
std::string getStyleName() const;
LatLng getDefaultLatLng() const;
diff --git a/include/mbgl/style/conversion/constant.hpp b/include/mbgl/style/conversion/constant.hpp
index 1e1fdc2ee8..07c0a35fae 100644
--- a/include/mbgl/style/conversion/constant.hpp
+++ b/include/mbgl/style/conversion/constant.hpp
@@ -4,6 +4,7 @@
#include <mbgl/util/optional.hpp>
#include <mbgl/util/color.hpp>
#include <mbgl/util/enum.hpp>
+#include <mbgl/util/string.hpp>
#include <array>
#include <string>
@@ -92,45 +93,25 @@ struct Converter<Color> {
}
};
-template <>
-struct Converter<std::array<float, 2>> {
+template <size_t N>
+struct Converter<std::array<float, N>> {
template <class V>
- optional<std::array<float, 2>> operator()(const V& value, Error& error) const {
- if (!isArray(value) || arrayLength(value) != 2) {
- error = { "value must be an array of two numbers" };
+ optional<std::array<float, N>> operator()(const V& value, Error& error) const {
+ if (!isArray(value) || arrayLength(value) != N) {
+ error = { "value must be an array of " + util::toString(N) + " numbers" };
return {};
}
- optional<float> first = toNumber(arrayMember(value, 0));
- optional<float> second = toNumber(arrayMember(value, 1));
- if (!first || !second) {
- error = { "value must be an array of two numbers" };
- return {};
- }
-
- return std::array<float, 2> {{ *first, *second }};
- }
-};
-
-template <>
-struct Converter<std::array<float, 4>> {
- template <class V>
- optional<std::array<float, 4>> operator()(const V& value, Error& error) const {
- if (!isArray(value) || arrayLength(value) != 4) {
- error = { "value must be an array of four numbers" };
- return {};
- }
-
- optional<float> first = toNumber(arrayMember(value, 0));
- optional<float> second = toNumber(arrayMember(value, 1));
- optional<float> third = toNumber(arrayMember(value, 2));
- optional<float> fourth = toNumber(arrayMember(value, 3));
- if (!first || !second) {
- error = { "value must be an array of four numbers" };
- return {};
+ std::array<float, N> result;
+ for (size_t i = 0; i < N; i++) {
+ optional<float> n = toNumber(arrayMember(value, i));
+ if (!n) {
+ error = { "value must be an array of " + util::toString(N) + " numbers" };
+ return {};
+ }
+ result[i] = *n;
}
-
- return std::array<float, 4> {{ *first, *second, *third, *fourth }};
+ return result;
}
};
diff --git a/include/mbgl/style/conversion/layer.hpp b/include/mbgl/style/conversion/layer.hpp
index efb1df8fef..3a64c36bf5 100644
--- a/include/mbgl/style/conversion/layer.hpp
+++ b/include/mbgl/style/conversion/layer.hpp
@@ -4,6 +4,7 @@
#include <mbgl/style/layers/background_layer.hpp>
#include <mbgl/style/layers/circle_layer.hpp>
#include <mbgl/style/layers/fill_layer.hpp>
+#include <mbgl/style/layers/fill_extrusion_layer.hpp>
#include <mbgl/style/layers/line_layer.hpp>
#include <mbgl/style/layers/raster_layer.hpp>
#include <mbgl/style/layers/symbol_layer.hpp>
@@ -92,6 +93,8 @@ public:
if (*type == "fill") {
converted = convertVectorLayer<FillLayer>(*id, value, error);
+ } else if (*type == "fill-extrusion") {
+ converted = convertVectorLayer<FillExtrusionLayer>(*id, value, error);
} else if (*type == "line") {
converted = convertVectorLayer<LineLayer>(*id, value, error);
} else if (*type == "circle") {
diff --git a/include/mbgl/style/conversion/light.hpp b/include/mbgl/style/conversion/light.hpp
new file mode 100644
index 0000000000..631ed04ccb
--- /dev/null
+++ b/include/mbgl/style/conversion/light.hpp
@@ -0,0 +1,122 @@
+#pragma once
+
+#include <mbgl/style/light.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/position.hpp>
+#include <mbgl/style/conversion/property_value.hpp>
+#include <mbgl/style/conversion/transition_options.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+template <>
+struct Converter<Light> {
+public:
+ template <class V>
+ optional<Light> operator()(const V& value, Error& error) const {
+ if (!isObject(value)) {
+ error = { "light must be an object" };
+ return {};
+ }
+
+ Light light;
+
+ const auto anchor = objectMember(value, "anchor");
+ if (anchor) {
+ optional<PropertyValue<LightAnchorType>> convertedAnchor =
+ convert<PropertyValue<LightAnchorType>>(*anchor, error);
+
+ if (convertedAnchor) {
+ light.get<LightAnchor>().value = *convertedAnchor;
+ } else {
+ return {};
+ }
+ }
+
+ const auto anchorTransition = objectMember(value, "anchor-transition");
+ if (anchorTransition) {
+ optional<TransitionOptions> transition =
+ convert<TransitionOptions>(*anchorTransition, error);
+ if (transition) {
+ light.get<LightAnchor>().transition = *transition;
+ } else {
+ return {};
+ }
+ }
+
+ const auto color = objectMember(value, "color");
+ if (color) {
+ optional<PropertyValue<Color>> convertedColor =
+ convert<PropertyValue<Color>>(*color, error);
+
+ if (convertedColor) {
+ light.get<LightColor>().value = *convertedColor;
+ } else {
+ return {};
+ }
+ }
+
+ const auto colorTransition = objectMember(value, "color-transition");
+ if (colorTransition) {
+ optional<TransitionOptions> transition =
+ convert<TransitionOptions>(*colorTransition, error);
+ if (transition) {
+ light.get<LightColor>().transition = *transition;
+ } else {
+ return {};
+ }
+ }
+
+ const auto position = objectMember(value, "position");
+ if (position) {
+ optional<PropertyValue<Position>> convertedPosition =
+ convert<PropertyValue<Position>>(*position, error);
+
+ if (convertedPosition) {
+ light.get<LightPosition>().value = *convertedPosition;
+ } else {
+ return {};
+ }
+ }
+
+ const auto positionTransition = objectMember(value, "position-transition");
+ if (positionTransition) {
+ optional<TransitionOptions> transition =
+ convert<TransitionOptions>(*positionTransition, error);
+ if (transition) {
+ light.get<LightPosition>().transition = *transition;
+ } else {
+ return {};
+ }
+ }
+
+ const auto intensity = objectMember(value, "intensity");
+ if (intensity) {
+ optional<PropertyValue<float>> convertedIntensity =
+ convert<PropertyValue<float>>(*intensity, error);
+
+ if (convertedIntensity) {
+ light.get<LightIntensity>().value = *convertedIntensity;
+ } else {
+ return {};
+ }
+ }
+
+ const auto intensityTransition = objectMember(value, "intensity-transition");
+ if (intensityTransition) {
+ optional<TransitionOptions> transition =
+ convert<TransitionOptions>(*intensityTransition, error);
+ if (transition) {
+ light.get<LightIntensity>().transition = *transition;
+ } else {
+ return {};
+ }
+ }
+ return { light };
+ };
+};
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/conversion/position.hpp b/include/mbgl/style/conversion/position.hpp
new file mode 100644
index 0000000000..7036b03822
--- /dev/null
+++ b/include/mbgl/style/conversion/position.hpp
@@ -0,0 +1,29 @@
+#pragma once
+
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/position.hpp>
+#include <mbgl/util/optional.hpp>
+
+#include <array>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+template <>
+struct Converter<Position> {
+ template <class V>
+ optional<Position> operator()(const V& value, Error& error) const {
+ optional<std::array<float, 3>> spherical = convert<std::array<float, 3>>(value, error);
+
+ if (!spherical) {
+ return {};
+ }
+
+ return Position(*spherical);
+ }
+};
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/layer.hpp b/include/mbgl/style/layer.hpp
index f09eb0165a..56f2c48fa7 100644
--- a/include/mbgl/style/layer.hpp
+++ b/include/mbgl/style/layer.hpp
@@ -66,7 +66,7 @@ public:
// Convenience method for dynamic dispatch on the concrete layer type. Using
// method overloading, this allows consolidation of logic common to vector-based
- // layers (Fill, Line, Circle, or Symbol). For example:
+ // layers (Fill, FillExtrusion, Line, Circle, or Symbol). For example:
//
// struct Visitor {
// void operator()(CustomLayer&) { ... }
diff --git a/include/mbgl/style/light.hpp b/include/mbgl/style/light.hpp
new file mode 100644
index 0000000000..bec8e6ddeb
--- /dev/null
+++ b/include/mbgl/style/light.hpp
@@ -0,0 +1,52 @@
+#pragma once
+
+#include <mbgl/style/property_value.hpp>
+#include <mbgl/style/transition_options.hpp>
+#include <mbgl/style/types.hpp>
+#include <mbgl/style/position.hpp>
+#include <mbgl/util/color.hpp>
+#include <mbgl/util/indexed_tuple.hpp>
+
+namespace mbgl {
+namespace style {
+
+template <class T>
+class LightProperty {
+public:
+ using Type = T;
+ using ValueType = PropertyValue<T>;
+
+ PropertyValue<T> value;
+ TransitionOptions transition;
+};
+
+struct LightAnchor : LightProperty<LightAnchorType> {
+ static LightAnchorType defaultValue() {
+ return LightAnchorType::Viewport;
+ }
+};
+
+struct LightPosition : LightProperty<Position> {
+ static Position defaultValue() {
+ std::array<float, 3> default_ = { { 1.15, 210, 30 } };
+ return Position{ { default_ } };
+ }
+};
+
+struct LightColor : LightProperty<Color> {
+ static Color defaultValue() {
+ return Color::white();
+ }
+};
+
+struct LightIntensity : LightProperty<float> {
+ static float defaultValue() {
+ return 0.5;
+ }
+};
+
+using LightProperties = TypeList<LightAnchor, LightPosition, LightColor, LightIntensity>;
+class Light : public IndexedTuple<LightProperties, LightProperties> {};
+
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/position.hpp b/include/mbgl/style/position.hpp
new file mode 100644
index 0000000000..3be8d1c55e
--- /dev/null
+++ b/include/mbgl/style/position.hpp
@@ -0,0 +1,68 @@
+#pragma once
+
+#include <mbgl/util/constants.hpp>
+
+#include <array>
+
+namespace mbgl {
+namespace style {
+class Position {
+public:
+ Position() = default;
+ Position(std::array<float, 3>& position_)
+ : radial(position_[0]), azimuthal(position_[1]), polar(position_[2]) {
+ calculateCartesian();
+ };
+
+ friend bool operator==(const Position& lhs, const Position& rhs) {
+ return lhs.radial == rhs.radial && lhs.azimuthal == rhs.azimuthal && lhs.polar == rhs.polar;
+ // TODO this doesn't address wrapping, which would be better addressed by comparing cartesian coordinates but being calculated floats are ont to be trusted.
+ }
+
+ friend bool operator!=(const Position& lhs, const Position& rhs) {
+ return !(lhs == rhs);
+ }
+
+ const std::array<float, 3> getCartesian() const {
+ return { { x, y, z } };
+ };
+
+ const std::array<float, 3> getSpherical() const {
+ return { { radial, azimuthal, polar } };
+ };
+
+ void set(std::array<float, 3>& position_) {
+ radial = position_[0];
+ azimuthal = position_[1];
+ polar = position_[2];
+ calculateCartesian();
+ };
+
+ // Utility function to be used only during interpolation; this leaves spherical coordinates undefined.
+ void setCartesian(std::array<float, 3>& position_) {
+ x = position_[0];
+ y = position_[1];
+ z = position_[2];
+ }
+
+private:
+ float radial;
+ float azimuthal;
+ float polar;
+ float x;
+ float y;
+ float z;
+
+ void calculateCartesian() {
+ // We abstract "north"/"up" (compass-wise) to be 0° when really this is 90° (π/2): we
+ // correct for that here
+ const float _a = (azimuthal + 90) * util::DEG2RAD;
+ const float _p = polar * util::DEG2RAD;
+
+ x = radial * std::cos(_a) * std::sin(_p);
+ y = radial * std::sin(_a) * std::sin(_p);
+ z = radial * std::cos(_p);
+ };
+};
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/types.hpp b/include/mbgl/style/types.hpp
index 1f2f5c3105..e0436efb67 100644
--- a/include/mbgl/style/types.hpp
+++ b/include/mbgl/style/types.hpp
@@ -92,5 +92,10 @@ enum class IconTextFitType : uint8_t {
Height
};
+enum class LightAnchorType: bool {
+ Map,
+ Viewport
+};
+
} // namespace style
} // namespace mbgl
diff --git a/src/mbgl/util/indexed_tuple.hpp b/include/mbgl/util/indexed_tuple.hpp
index a414639530..a414639530 100644
--- a/src/mbgl/util/indexed_tuple.hpp
+++ b/include/mbgl/util/indexed_tuple.hpp
diff --git a/include/mbgl/util/interpolate.hpp b/include/mbgl/util/interpolate.hpp
index d463ffc056..a2103f18b2 100644
--- a/include/mbgl/util/interpolate.hpp
+++ b/include/mbgl/util/interpolate.hpp
@@ -2,6 +2,7 @@
#include <mbgl/util/color.hpp>
#include <mbgl/util/range.hpp>
+#include <mbgl/style/position.hpp>
#include <array>
#include <vector>
@@ -47,6 +48,17 @@ public:
};
template <>
+struct Interpolator<style::Position> {
+public:
+ style::Position operator()(const style::Position& a, const style::Position& b, const double t) {
+ auto pos = style::Position();
+ auto interpolated = interpolate(a.getCartesian(), b.getCartesian(), t);
+ pos.setCartesian(interpolated);
+ return { pos };
+ }
+};
+
+template <>
struct Interpolator<Color> {
public:
Color operator()(const Color& a, const Color& b, const double t) {
diff --git a/src/mbgl/util/type_list.hpp b/include/mbgl/util/type_list.hpp
index 4a5e95c8a4..4a5e95c8a4 100644
--- a/src/mbgl/util/type_list.hpp
+++ b/include/mbgl/util/type_list.hpp
diff --git a/mapbox-gl-js b/mapbox-gl-js
-Subproject d7945c30a07b5e733ded00036136fd7bf543497
+Subproject d3b39cbba4f09d26ef51159aa56270e67006c51
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillExtrusionLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillExtrusionLayer.java
new file mode 100644
index 0000000000..6772da73b1
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillExtrusionLayer.java
@@ -0,0 +1,339 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`.
+
+package com.mapbox.mapboxsdk.style.layers;
+
+import android.support.annotation.ColorInt;
+import android.support.annotation.NonNull;
+import android.support.annotation.UiThread;
+
+import static com.mapbox.mapboxsdk.utils.ColorUtils.rgbaToColor;
+
+import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
+
+/**
+ * An extruded (3D) polygon.
+ *
+ * @see <a href="https://www.mapbox.com/mapbox-gl-style-spec/#layers-fill-extrusion">The online documentation</a>
+ */
+@UiThread
+public class FillExtrusionLayer extends Layer {
+
+ /**
+ * Creates a FillExtrusionLayer.
+ *
+ * @param nativePtr pointer used by core
+ */
+ public FillExtrusionLayer(long nativePtr) {
+ super(nativePtr);
+ }
+
+ /**
+ * Creates a FillExtrusionLayer.
+ *
+ * @param layerId the id of the layer
+ * @param sourceId the id of the source
+ */
+ public FillExtrusionLayer(String layerId, String sourceId) {
+ initialize(layerId, sourceId);
+ }
+
+ protected native void initialize(String layerId, String sourceId);
+
+ /**
+ * Set the source layer.
+ *
+ * @param sourceLayer the source layer to set
+ */
+ public void setSourceLayer(String sourceLayer) {
+ nativeSetSourceLayer(sourceLayer);
+ }
+
+ /**
+ * Set the source Layer.
+ *
+ * @param sourceLayer the source layer to set
+ * @return This
+ */
+ public FillExtrusionLayer withSourceLayer(String sourceLayer) {
+ setSourceLayer(sourceLayer);
+ return this;
+ }
+
+ /**
+ * Get the source layer.
+ *
+ * @return sourceLayer the source layer to get
+ */
+ public String getSourceLayer() {
+ return nativeGetSourceLayer();
+ }
+
+ /**
+ * Set a single filter.
+ *
+ * @param filter the filter to set
+ */
+ public void setFilter(Filter.Statement filter) {
+ nativeSetFilter(filter.toArray());
+ }
+
+ /**
+ * Set a single filter.
+ *
+ * @param filter the filter to set
+ * @return This
+ */
+ public FillExtrusionLayer withFilter(Filter.Statement filter) {
+ setFilter(filter);
+ return this;
+ }
+
+ /**
+ * Set a property or properties.
+ *
+ * @param properties the var-args properties
+ * @return This
+ */
+ public FillExtrusionLayer withProperties(@NonNull PropertyValue<?>... properties) {
+ setProperties(properties);
+ return this;
+ }
+
+ // Property getters
+
+ /**
+ * Get the FillExtrusionOpacity property
+ *
+ * @return property wrapper value around Float
+ */
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getFillExtrusionOpacity() {
+ return (PropertyValue<Float>) new PropertyValue("fill-extrusion-opacity", nativeGetFillExtrusionOpacity());
+ }
+
+ /**
+ * Get the FillExtrusionOpacity property transition options
+ *
+ * @return transition options for Float
+ */
+ public TransitionOptions getFillExtrusionOpacityTransition() {
+ return nativeGetFillExtrusionOpacityTransition();
+ }
+
+ /**
+ * Set the FillExtrusionOpacity property transition options
+ *
+ * @param options transition options for Float
+ */
+ public void setFillExtrusionOpacityTransition(TransitionOptions options) {
+ nativeSetFillExtrusionOpacityTransition(options.getDuration(), options.getDelay());
+ }
+
+ /**
+ * Get the FillExtrusionColor property
+ *
+ * @return property wrapper value around String
+ */
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getFillExtrusionColor() {
+ return (PropertyValue<String>) new PropertyValue("fill-extrusion-color", nativeGetFillExtrusionColor());
+ }
+
+ /**
+ * The base color of the extruded fill. The extrusion's surfaces will be shaded differently based on this color in combination with the root `light` settings. If this color is specified as `rgba` with an alpha component, the alpha component will be ignored; use `fill-extrusion-opacity` to set layer opacity.
+ *
+ * @return int representation of a rgba string color
+ * @throws RuntimeException thrown if property isn't a value
+ */
+ @ColorInt
+ public int getFillExtrusionColorAsInt() {
+ PropertyValue<String> value = getFillExtrusionColor();
+ if (value.isValue()) {
+ return rgbaToColor(value.getValue());
+ } else {
+ throw new RuntimeException("fill-extrusion-color was set as a Function");
+ }
+ }
+
+ /**
+ * Get the FillExtrusionColor property transition options
+ *
+ * @return transition options for String
+ */
+ public TransitionOptions getFillExtrusionColorTransition() {
+ return nativeGetFillExtrusionColorTransition();
+ }
+
+ /**
+ * Set the FillExtrusionColor property transition options
+ *
+ * @param options transition options for String
+ */
+ public void setFillExtrusionColorTransition(TransitionOptions options) {
+ nativeSetFillExtrusionColorTransition(options.getDuration(), options.getDelay());
+ }
+
+ /**
+ * Get the FillExtrusionTranslate property
+ *
+ * @return property wrapper value around Float[]
+ */
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float[]> getFillExtrusionTranslate() {
+ return (PropertyValue<Float[]>) new PropertyValue("fill-extrusion-translate", nativeGetFillExtrusionTranslate());
+ }
+
+ /**
+ * Get the FillExtrusionTranslate property transition options
+ *
+ * @return transition options for Float[]
+ */
+ public TransitionOptions getFillExtrusionTranslateTransition() {
+ return nativeGetFillExtrusionTranslateTransition();
+ }
+
+ /**
+ * Set the FillExtrusionTranslate property transition options
+ *
+ * @param options transition options for Float[]
+ */
+ public void setFillExtrusionTranslateTransition(TransitionOptions options) {
+ nativeSetFillExtrusionTranslateTransition(options.getDuration(), options.getDelay());
+ }
+
+ /**
+ * Get the FillExtrusionTranslateAnchor property
+ *
+ * @return property wrapper value around String
+ */
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getFillExtrusionTranslateAnchor() {
+ return (PropertyValue<String>) new PropertyValue("fill-extrusion-translate-anchor", nativeGetFillExtrusionTranslateAnchor());
+ }
+
+ /**
+ * Get the FillExtrusionPattern property
+ *
+ * @return property wrapper value around String
+ */
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getFillExtrusionPattern() {
+ return (PropertyValue<String>) new PropertyValue("fill-extrusion-pattern", nativeGetFillExtrusionPattern());
+ }
+
+ /**
+ * Get the FillExtrusionPattern property transition options
+ *
+ * @return transition options for String
+ */
+ public TransitionOptions getFillExtrusionPatternTransition() {
+ return nativeGetFillExtrusionPatternTransition();
+ }
+
+ /**
+ * Set the FillExtrusionPattern property transition options
+ *
+ * @param options transition options for String
+ */
+ public void setFillExtrusionPatternTransition(TransitionOptions options) {
+ nativeSetFillExtrusionPatternTransition(options.getDuration(), options.getDelay());
+ }
+
+ /**
+ * Get the FillExtrusionHeight property
+ *
+ * @return property wrapper value around Float
+ */
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getFillExtrusionHeight() {
+ return (PropertyValue<Float>) new PropertyValue("fill-extrusion-height", nativeGetFillExtrusionHeight());
+ }
+
+ /**
+ * Get the FillExtrusionHeight property transition options
+ *
+ * @return transition options for Float
+ */
+ public TransitionOptions getFillExtrusionHeightTransition() {
+ return nativeGetFillExtrusionHeightTransition();
+ }
+
+ /**
+ * Set the FillExtrusionHeight property transition options
+ *
+ * @param options transition options for Float
+ */
+ public void setFillExtrusionHeightTransition(TransitionOptions options) {
+ nativeSetFillExtrusionHeightTransition(options.getDuration(), options.getDelay());
+ }
+
+ /**
+ * Get the FillExtrusionBase property
+ *
+ * @return property wrapper value around Float
+ */
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getFillExtrusionBase() {
+ return (PropertyValue<Float>) new PropertyValue("fill-extrusion-base", nativeGetFillExtrusionBase());
+ }
+
+ /**
+ * Get the FillExtrusionBase property transition options
+ *
+ * @return transition options for Float
+ */
+ public TransitionOptions getFillExtrusionBaseTransition() {
+ return nativeGetFillExtrusionBaseTransition();
+ }
+
+ /**
+ * Set the FillExtrusionBase property transition options
+ *
+ * @param options transition options for Float
+ */
+ public void setFillExtrusionBaseTransition(TransitionOptions options) {
+ nativeSetFillExtrusionBaseTransition(options.getDuration(), options.getDelay());
+ }
+
+ private native Object nativeGetFillExtrusionOpacity();
+
+ private native TransitionOptions nativeGetFillExtrusionOpacityTransition();
+
+ private native void nativeSetFillExtrusionOpacityTransition(long duration, long delay);
+
+ private native Object nativeGetFillExtrusionColor();
+
+ private native TransitionOptions nativeGetFillExtrusionColorTransition();
+
+ private native void nativeSetFillExtrusionColorTransition(long duration, long delay);
+
+ private native Object nativeGetFillExtrusionTranslate();
+
+ private native TransitionOptions nativeGetFillExtrusionTranslateTransition();
+
+ private native void nativeSetFillExtrusionTranslateTransition(long duration, long delay);
+
+ private native Object nativeGetFillExtrusionTranslateAnchor();
+
+ private native Object nativeGetFillExtrusionPattern();
+
+ private native TransitionOptions nativeGetFillExtrusionPatternTransition();
+
+ private native void nativeSetFillExtrusionPatternTransition(long duration, long delay);
+
+ private native Object nativeGetFillExtrusionHeight();
+
+ private native TransitionOptions nativeGetFillExtrusionHeightTransition();
+
+ private native void nativeSetFillExtrusionHeightTransition(long duration, long delay);
+
+ private native Object nativeGetFillExtrusionBase();
+
+ private native TransitionOptions nativeGetFillExtrusionBaseTransition();
+
+ private native void nativeSetFillExtrusionBaseTransition(long duration, long delay);
+
+ @Override
+ protected native void finalize() throws Throwable;
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java
index 641ee551a2..48e0ec5de3 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java
@@ -446,6 +446,27 @@ public final class Property {
@Retention(RetentionPolicy.SOURCE)
public @interface CIRCLE_PITCH_SCALE {}
+ // FILL_EXTRUSION_TRANSLATE_ANCHOR: Controls the translation reference point.
+
+ /**
+ * The fill extrusion is translated relative to the map.
+ */
+ public static final String FILL_EXTRUSION_TRANSLATE_ANCHOR_MAP = "map";
+ /**
+ * The fill extrusion is translated relative to the viewport.
+ */
+ public static final String FILL_EXTRUSION_TRANSLATE_ANCHOR_VIEWPORT = "viewport";
+
+ /**
+ * Controls the translation reference point.
+ */
+ @StringDef({
+ FILL_EXTRUSION_TRANSLATE_ANCHOR_MAP,
+ FILL_EXTRUSION_TRANSLATE_ANCHOR_VIEWPORT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FILL_EXTRUSION_TRANSLATE_ANCHOR {}
+
private Property() {
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java
index e37245000e..e4ea9676fa 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java
@@ -1029,6 +1029,170 @@ public class PropertyFactory {
}
/**
+ * The opacity of the entire fill extrusion layer. This is rendered on a per-layer, not per-feature, basis, and data-driven styling is not available.
+ *
+ * @param value a Float value
+ * @return property wrapper around Float
+ */
+ public static PropertyValue<Float> fillExtrusionOpacity(Float value) {
+ return new PaintPropertyValue<>("fill-extrusion-opacity", value);
+ }
+
+
+ /**
+ * The opacity of the entire fill extrusion layer. This is rendered on a per-layer, not per-feature, basis, and data-driven styling is not available.
+ *
+ * @param <Z> the zoom parameter type
+ * @param function a wrapper {@link CameraFunction} for Float
+ * @return property wrapper around a Float function
+ */
+ public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> fillExtrusionOpacity(CameraFunction<Z, Float> function) {
+ return new PaintPropertyValue<>("fill-extrusion-opacity", function);
+ }
+
+ /**
+ * The base color of the extruded fill. The extrusion's surfaces will be shaded differently based on this color in combination with the root `light` settings. If this color is specified as `rgba` with an alpha component, the alpha component will be ignored; use {@link PropertyFactory#fillExtrusionOpacity} to set layer opacity.
+ *
+ * @param value a int color value
+ * @return property wrapper around String color
+ */
+ public static PropertyValue<String> fillExtrusionColor(@ColorInt int value) {
+ return new PaintPropertyValue<>("fill-extrusion-color", colorToRgbaString(value));
+ }
+
+ /**
+ * The base color of the extruded fill. The extrusion's surfaces will be shaded differently based on this color in combination with the root `light` settings. If this color is specified as `rgba` with an alpha component, the alpha component will be ignored; use {@link PropertyFactory#fillExtrusionOpacity} to set layer opacity.
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<String> fillExtrusionColor(String value) {
+ return new PaintPropertyValue<>("fill-extrusion-color", value);
+ }
+
+
+ /**
+ * The base color of the extruded fill. The extrusion's surfaces will be shaded differently based on this color in combination with the root `light` settings. If this color is specified as `rgba` with an alpha component, the alpha component will be ignored; use {@link PropertyFactory#fillExtrusionOpacity} to set layer opacity.
+ *
+ * @param <T> the function input type
+ * @param function a wrapper function for String
+ * @return property wrapper around a String function
+ */
+ public static <T> PropertyValue<Function<T, String>> fillExtrusionColor(Function<T, String> function) {
+ return new PaintPropertyValue<>("fill-extrusion-color", function);
+ }
+
+ /**
+ * The geometry's offset. Values are [x, y] where negatives indicate left and up (on the flat plane), respectively.
+ *
+ * @param value a Float[] value
+ * @return property wrapper around Float[]
+ */
+ public static PropertyValue<Float[]> fillExtrusionTranslate(Float[] value) {
+ return new PaintPropertyValue<>("fill-extrusion-translate", value);
+ }
+
+
+ /**
+ * The geometry's offset. Values are [x, y] where negatives indicate left and up (on the flat plane), respectively.
+ *
+ * @param <Z> the zoom parameter type
+ * @param function a wrapper {@link CameraFunction} for Float[]
+ * @return property wrapper around a Float[] function
+ */
+ public static <Z extends Number> PropertyValue<CameraFunction<Z, Float[]>> fillExtrusionTranslate(CameraFunction<Z, Float[]> function) {
+ return new PaintPropertyValue<>("fill-extrusion-translate", function);
+ }
+
+ /**
+ * Controls the translation reference point.
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<String> fillExtrusionTranslateAnchor(@Property.FILL_EXTRUSION_TRANSLATE_ANCHOR String value) {
+ return new PaintPropertyValue<>("fill-extrusion-translate-anchor", value);
+ }
+
+
+ /**
+ * Controls the translation reference point.
+ *
+ * @param <Z> the zoom parameter type
+ * @param function a wrapper {@link CameraFunction} for String
+ * @return property wrapper around a String function
+ */
+ public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> fillExtrusionTranslateAnchor(CameraFunction<Z, String> function) {
+ return new PaintPropertyValue<>("fill-extrusion-translate-anchor", function);
+ }
+
+ /**
+ * Name of image in sprite to use for drawing images on extruded fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512).
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<String> fillExtrusionPattern(String value) {
+ return new PaintPropertyValue<>("fill-extrusion-pattern", value);
+ }
+
+
+ /**
+ * Name of image in sprite to use for drawing images on extruded fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512).
+ *
+ * @param <Z> the zoom parameter type
+ * @param function a wrapper {@link CameraFunction} for String
+ * @return property wrapper around a String function
+ */
+ public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> fillExtrusionPattern(CameraFunction<Z, String> function) {
+ return new PaintPropertyValue<>("fill-extrusion-pattern", function);
+ }
+
+ /**
+ * The height with which to extrude this layer.
+ *
+ * @param value a Float value
+ * @return property wrapper around Float
+ */
+ public static PropertyValue<Float> fillExtrusionHeight(Float value) {
+ return new PaintPropertyValue<>("fill-extrusion-height", value);
+ }
+
+
+ /**
+ * The height with which to extrude this layer.
+ *
+ * @param <T> the function input type
+ * @param function a wrapper function for Float
+ * @return property wrapper around a Float function
+ */
+ public static <T> PropertyValue<Function<T, Float>> fillExtrusionHeight(Function<T, Float> function) {
+ return new PaintPropertyValue<>("fill-extrusion-height", function);
+ }
+
+ /**
+ * The height with which to extrude the base of this layer. Must be less than or equal to {@link PropertyFactory#fillExtrusionHeight}.
+ *
+ * @param value a Float value
+ * @return property wrapper around Float
+ */
+ public static PropertyValue<Float> fillExtrusionBase(Float value) {
+ return new PaintPropertyValue<>("fill-extrusion-base", value);
+ }
+
+
+ /**
+ * The height with which to extrude the base of this layer. Must be less than or equal to {@link PropertyFactory#fillExtrusionHeight}.
+ *
+ * @param <T> the function input type
+ * @param function a wrapper function for Float
+ * @return property wrapper around a Float function
+ */
+ public static <T> PropertyValue<Function<T, Float>> fillExtrusionBase(Function<T, Float> function) {
+ return new PaintPropertyValue<>("fill-extrusion-base", function);
+ }
+
+ /**
* The opacity at which the image will be drawn.
*
* @param value a Float value
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/BackgroundLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/BackgroundLayerTest.java
index 5bab1de904..851660f06e 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/BackgroundLayerTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/BackgroundLayerTest.java
@@ -3,33 +3,42 @@
package com.mapbox.mapboxsdk.testapp.style;
import android.graphics.Color;
+import android.support.test.espresso.Espresso;
+import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
+import timber.log.Timber;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.style.functions.CompositeFunction;
import com.mapbox.mapboxsdk.style.functions.CameraFunction;
+import com.mapbox.mapboxsdk.style.functions.SourceFunction;
+import com.mapbox.mapboxsdk.style.functions.stops.CategoricalStops;
import com.mapbox.mapboxsdk.style.functions.stops.ExponentialStops;
+import com.mapbox.mapboxsdk.style.functions.stops.IdentityStops;
import com.mapbox.mapboxsdk.style.functions.stops.IntervalStops;
+import com.mapbox.mapboxsdk.style.functions.stops.Stop;
+import com.mapbox.mapboxsdk.style.functions.stops.Stops;
import com.mapbox.mapboxsdk.style.layers.BackgroundLayer;
-import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.activity.style.RuntimeStyleTestActivity;
+import com.mapbox.mapboxsdk.testapp.utils.OnMapReadyIdlingResource;
import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest;
-import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import timber.log.Timber;
-
-import static com.mapbox.mapboxsdk.style.functions.Function.zoom;
+import static com.mapbox.mapboxsdk.style.functions.Function.*;
import static com.mapbox.mapboxsdk.style.functions.stops.Stop.stop;
-import static com.mapbox.mapboxsdk.style.functions.stops.Stops.exponential;
-import static com.mapbox.mapboxsdk.style.functions.stops.Stops.interval;
-import static com.mapbox.mapboxsdk.style.layers.Property.NONE;
-import static com.mapbox.mapboxsdk.style.layers.Property.VISIBLE;
-import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.backgroundColor;
-import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.backgroundOpacity;
-import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.backgroundPattern;
-import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.visibility;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
+import static com.mapbox.mapboxsdk.style.functions.stops.Stops.*;
+import static org.junit.Assert.*;
+import static com.mapbox.mapboxsdk.style.layers.Property.*;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.*;
+
+import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
+import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity;
/**
* Basic smoke tests for BackgroundLayer
@@ -44,7 +53,7 @@ public class BackgroundLayerTest extends BaseActivityTest {
return EspressoTestActivity.class;
}
- private void setupLayer() {
+ private void setupLayer(){
Timber.i("Retrieving layer");
layer = mapboxMap.getLayerAs("background");
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillExtrusionLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillExtrusionLayerTest.java
new file mode 100644
index 0000000000..fec9a6c119
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillExtrusionLayerTest.java
@@ -0,0 +1,760 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`.
+
+package com.mapbox.mapboxsdk.testapp.style;
+
+import android.graphics.Color;
+import android.support.test.espresso.Espresso;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import timber.log.Timber;
+
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.style.functions.CompositeFunction;
+import com.mapbox.mapboxsdk.style.functions.CameraFunction;
+import com.mapbox.mapboxsdk.style.functions.SourceFunction;
+import com.mapbox.mapboxsdk.style.functions.stops.CategoricalStops;
+import com.mapbox.mapboxsdk.style.functions.stops.ExponentialStops;
+import com.mapbox.mapboxsdk.style.functions.stops.IdentityStops;
+import com.mapbox.mapboxsdk.style.functions.stops.IntervalStops;
+import com.mapbox.mapboxsdk.style.functions.stops.Stop;
+import com.mapbox.mapboxsdk.style.functions.stops.Stops;
+import com.mapbox.mapboxsdk.style.layers.FillExtrusionLayer;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.activity.style.RuntimeStyleTestActivity;
+import com.mapbox.mapboxsdk.testapp.utils.OnMapReadyIdlingResource;
+import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static com.mapbox.mapboxsdk.style.functions.Function.*;
+import static com.mapbox.mapboxsdk.style.functions.stops.Stop.stop;
+import static com.mapbox.mapboxsdk.style.functions.stops.Stops.*;
+import static org.junit.Assert.*;
+import static com.mapbox.mapboxsdk.style.layers.Property.*;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.*;
+
+import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
+import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity;
+
+/**
+ * Basic smoke tests for FillExtrusionLayer
+ */
+@RunWith(AndroidJUnit4.class)
+public class FillExtrusionLayerTest extends BaseActivityTest {
+
+ private FillExtrusionLayer layer;
+
+ @Override
+ protected Class getActivityClass() {
+ return EspressoTestActivity.class;
+ }
+
+ private void setupLayer(){
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Timber.i("Adding layer");
+ layer = new FillExtrusionLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ // Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
+ }
+ }
+
+ @Test
+ public void testSetVisibility() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("Visibility");
+ assertNotNull(layer);
+
+ // Get initial
+ assertEquals(layer.getVisibility().getValue(), VISIBLE);
+
+ // Set
+ layer.setProperties(visibility(NONE));
+ assertEquals(layer.getVisibility().getValue(), NONE);
+ }
+
+ @Test
+ public void testSourceLayer() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("SourceLayer");
+ assertNotNull(layer);
+
+ // Get initial
+ assertEquals(layer.getSourceLayer(), "composite");
+
+ // Set
+ final String sourceLayer = "test";
+ layer.setSourceLayer(sourceLayer);
+ assertEquals(layer.getSourceLayer(), sourceLayer);
+ }
+
+ @Test
+ public void testFillExtrusionOpacityTransition() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("fill-extrusion-opacityTransitionOptions");
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setFillExtrusionOpacityTransition(options);
+ assertEquals(layer.getFillExtrusionOpacityTransition(), options);
+ }
+
+ @Test
+ public void testFillExtrusionOpacityAsConstant() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("fill-extrusion-opacity");
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(fillExtrusionOpacity(0.3f));
+ assertEquals((Float) layer.getFillExtrusionOpacity().getValue(), (Float) 0.3f);
+ }
+
+ @Test
+ public void testFillExtrusionOpacityAsCameraFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("fill-extrusion-opacity");
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ fillExtrusionOpacity(
+ zoom(
+ exponential(
+ stop(2, fillExtrusionOpacity(0.3f))
+ ).withBase(0.5f)
+ )
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getFillExtrusionOpacity());
+ assertNotNull(layer.getFillExtrusionOpacity().getFunction());
+ assertEquals(CameraFunction.class, layer.getFillExtrusionOpacity().getFunction().getClass());
+ assertEquals(ExponentialStops.class, layer.getFillExtrusionOpacity().getFunction().getStops().getClass());
+ assertEquals(0.5f, ((ExponentialStops) layer.getFillExtrusionOpacity().getFunction().getStops()).getBase(), 0.001);
+ assertEquals(1, ((ExponentialStops) layer.getFillExtrusionOpacity().getFunction().getStops()).size());
+ }
+
+ @Test
+ public void testFillExtrusionColorTransition() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("fill-extrusion-colorTransitionOptions");
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setFillExtrusionColorTransition(options);
+ assertEquals(layer.getFillExtrusionColorTransition(), options);
+ }
+
+ @Test
+ public void testFillExtrusionColorAsConstant() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("fill-extrusion-color");
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(fillExtrusionColor("rgba(0, 0, 0, 1)"));
+ assertEquals((String) layer.getFillExtrusionColor().getValue(), (String) "rgba(0, 0, 0, 1)");
+ }
+
+ @Test
+ public void testFillExtrusionColorAsCameraFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("fill-extrusion-color");
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ fillExtrusionColor(
+ zoom(
+ exponential(
+ stop(2, fillExtrusionColor("rgba(0, 0, 0, 1)"))
+ ).withBase(0.5f)
+ )
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getFillExtrusionColor());
+ assertNotNull(layer.getFillExtrusionColor().getFunction());
+ assertEquals(CameraFunction.class, layer.getFillExtrusionColor().getFunction().getClass());
+ assertEquals(ExponentialStops.class, layer.getFillExtrusionColor().getFunction().getStops().getClass());
+ assertEquals(0.5f, ((ExponentialStops) layer.getFillExtrusionColor().getFunction().getStops()).getBase(), 0.001);
+ assertEquals(1, ((ExponentialStops) layer.getFillExtrusionColor().getFunction().getStops()).size());
+ }
+
+ @Test
+ public void testFillExtrusionColorAsIdentitySourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("fill-extrusion-color");
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ fillExtrusionColor(property("FeaturePropertyA", Stops.<String>identity()))
+ );
+
+ // Verify
+ assertNotNull(layer.getFillExtrusionColor());
+ assertNotNull(layer.getFillExtrusionColor().getFunction());
+ assertEquals(SourceFunction.class, layer.getFillExtrusionColor().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getFillExtrusionColor().getFunction()).getProperty());
+ assertEquals(IdentityStops.class, layer.getFillExtrusionColor().getFunction().getStops().getClass());
+ }
+
+ @Test
+ public void testFillExtrusionColorAsExponentialSourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("fill-extrusion-color");
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ fillExtrusionColor(
+ property(
+ "FeaturePropertyA",
+ exponential(
+ stop(Color.RED, fillExtrusionColor(Color.RED))
+ ).withBase(0.5f)
+ )
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getFillExtrusionColor());
+ assertNotNull(layer.getFillExtrusionColor().getFunction());
+ assertEquals(SourceFunction.class, layer.getFillExtrusionColor().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getFillExtrusionColor().getFunction()).getProperty());
+ assertEquals(ExponentialStops.class, layer.getFillExtrusionColor().getFunction().getStops().getClass());
+ }
+
+ @Test
+ public void testFillExtrusionColorAsCategoricalSourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("fill-extrusion-color");
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ fillExtrusionColor(
+ property(
+ "FeaturePropertyA",
+ categorical(
+ stop("valueA", fillExtrusionColor(Color.RED))
+ )
+ ).withDefaultValue(fillExtrusionColor(Color.GREEN))
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getFillExtrusionColor());
+ assertNotNull(layer.getFillExtrusionColor().getFunction());
+ assertEquals(SourceFunction.class, layer.getFillExtrusionColor().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getFillExtrusionColor().getFunction()).getProperty());
+ assertEquals(CategoricalStops.class, layer.getFillExtrusionColor().getFunction().getStops().getClass());
+ assertNotNull(((SourceFunction) layer.getFillExtrusionColor().getFunction()).getDefaultValue());
+ assertNotNull(((SourceFunction) layer.getFillExtrusionColor().getFunction()).getDefaultValue().getValue());
+ assertEquals(Color.GREEN, (int) ((SourceFunction) layer.getFillExtrusionColor().getFunction()).getDefaultValue().getColorInt());
+ }
+
+ @Test
+ public void testFillExtrusionColorAsIntConstant() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("fill-extrusion-color");
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(fillExtrusionColor(Color.RED));
+ assertEquals(layer.getFillExtrusionColorAsInt(), Color.RED);
+ }
+
+ @Test
+ public void testFillExtrusionTranslateTransition() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("fill-extrusion-translateTransitionOptions");
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setFillExtrusionTranslateTransition(options);
+ assertEquals(layer.getFillExtrusionTranslateTransition(), options);
+ }
+
+ @Test
+ public void testFillExtrusionTranslateAsConstant() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("fill-extrusion-translate");
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(fillExtrusionTranslate(new Float[]{0f,0f}));
+ assertEquals((Float[]) layer.getFillExtrusionTranslate().getValue(), (Float[]) new Float[]{0f,0f});
+ }
+
+ @Test
+ public void testFillExtrusionTranslateAsCameraFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("fill-extrusion-translate");
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ fillExtrusionTranslate(
+ zoom(
+ exponential(
+ stop(2, fillExtrusionTranslate(new Float[]{0f,0f}))
+ ).withBase(0.5f)
+ )
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getFillExtrusionTranslate());
+ assertNotNull(layer.getFillExtrusionTranslate().getFunction());
+ assertEquals(CameraFunction.class, layer.getFillExtrusionTranslate().getFunction().getClass());
+ assertEquals(ExponentialStops.class, layer.getFillExtrusionTranslate().getFunction().getStops().getClass());
+ assertEquals(0.5f, ((ExponentialStops) layer.getFillExtrusionTranslate().getFunction().getStops()).getBase(), 0.001);
+ assertEquals(1, ((ExponentialStops) layer.getFillExtrusionTranslate().getFunction().getStops()).size());
+ }
+
+ @Test
+ public void testFillExtrusionTranslateAnchorAsConstant() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("fill-extrusion-translate-anchor");
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(fillExtrusionTranslateAnchor(FILL_EXTRUSION_TRANSLATE_ANCHOR_MAP));
+ assertEquals((String) layer.getFillExtrusionTranslateAnchor().getValue(), (String) FILL_EXTRUSION_TRANSLATE_ANCHOR_MAP);
+ }
+
+ @Test
+ public void testFillExtrusionTranslateAnchorAsCameraFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("fill-extrusion-translate-anchor");
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ fillExtrusionTranslateAnchor(
+ zoom(
+ interval(
+ stop(2, fillExtrusionTranslateAnchor(FILL_EXTRUSION_TRANSLATE_ANCHOR_MAP))
+ )
+ )
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getFillExtrusionTranslateAnchor());
+ assertNotNull(layer.getFillExtrusionTranslateAnchor().getFunction());
+ assertEquals(CameraFunction.class, layer.getFillExtrusionTranslateAnchor().getFunction().getClass());
+ assertEquals(IntervalStops.class, layer.getFillExtrusionTranslateAnchor().getFunction().getStops().getClass());
+ assertEquals(1, ((IntervalStops) layer.getFillExtrusionTranslateAnchor().getFunction().getStops()).size());
+ }
+
+ @Test
+ public void testFillExtrusionPatternTransition() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("fill-extrusion-patternTransitionOptions");
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setFillExtrusionPatternTransition(options);
+ assertEquals(layer.getFillExtrusionPatternTransition(), options);
+ }
+
+ @Test
+ public void testFillExtrusionPatternAsConstant() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("fill-extrusion-pattern");
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(fillExtrusionPattern("pedestrian-polygon"));
+ assertEquals((String) layer.getFillExtrusionPattern().getValue(), (String) "pedestrian-polygon");
+ }
+
+ @Test
+ public void testFillExtrusionPatternAsCameraFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("fill-extrusion-pattern");
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ fillExtrusionPattern(
+ zoom(
+ interval(
+ stop(2, fillExtrusionPattern("pedestrian-polygon"))
+ )
+ )
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getFillExtrusionPattern());
+ assertNotNull(layer.getFillExtrusionPattern().getFunction());
+ assertEquals(CameraFunction.class, layer.getFillExtrusionPattern().getFunction().getClass());
+ assertEquals(IntervalStops.class, layer.getFillExtrusionPattern().getFunction().getStops().getClass());
+ assertEquals(1, ((IntervalStops) layer.getFillExtrusionPattern().getFunction().getStops()).size());
+ }
+
+ @Test
+ public void testFillExtrusionHeightTransition() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("fill-extrusion-heightTransitionOptions");
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setFillExtrusionHeightTransition(options);
+ assertEquals(layer.getFillExtrusionHeightTransition(), options);
+ }
+
+ @Test
+ public void testFillExtrusionHeightAsConstant() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("fill-extrusion-height");
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(fillExtrusionHeight(0.3f));
+ assertEquals((Float) layer.getFillExtrusionHeight().getValue(), (Float) 0.3f);
+ }
+
+ @Test
+ public void testFillExtrusionHeightAsCameraFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("fill-extrusion-height");
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ fillExtrusionHeight(
+ zoom(
+ exponential(
+ stop(2, fillExtrusionHeight(0.3f))
+ ).withBase(0.5f)
+ )
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getFillExtrusionHeight());
+ assertNotNull(layer.getFillExtrusionHeight().getFunction());
+ assertEquals(CameraFunction.class, layer.getFillExtrusionHeight().getFunction().getClass());
+ assertEquals(ExponentialStops.class, layer.getFillExtrusionHeight().getFunction().getStops().getClass());
+ assertEquals(0.5f, ((ExponentialStops) layer.getFillExtrusionHeight().getFunction().getStops()).getBase(), 0.001);
+ assertEquals(1, ((ExponentialStops) layer.getFillExtrusionHeight().getFunction().getStops()).size());
+ }
+
+ @Test
+ public void testFillExtrusionHeightAsIdentitySourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("fill-extrusion-height");
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ fillExtrusionHeight(property("FeaturePropertyA", Stops.<Float>identity()))
+ );
+
+ // Verify
+ assertNotNull(layer.getFillExtrusionHeight());
+ assertNotNull(layer.getFillExtrusionHeight().getFunction());
+ assertEquals(SourceFunction.class, layer.getFillExtrusionHeight().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getFillExtrusionHeight().getFunction()).getProperty());
+ assertEquals(IdentityStops.class, layer.getFillExtrusionHeight().getFunction().getStops().getClass());
+ }
+
+ @Test
+ public void testFillExtrusionHeightAsExponentialSourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("fill-extrusion-height");
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ fillExtrusionHeight(
+ property(
+ "FeaturePropertyA",
+ exponential(
+ stop(0.3f, fillExtrusionHeight(0.3f))
+ ).withBase(0.5f)
+ )
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getFillExtrusionHeight());
+ assertNotNull(layer.getFillExtrusionHeight().getFunction());
+ assertEquals(SourceFunction.class, layer.getFillExtrusionHeight().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getFillExtrusionHeight().getFunction()).getProperty());
+ assertEquals(ExponentialStops.class, layer.getFillExtrusionHeight().getFunction().getStops().getClass());
+ }
+
+ @Test
+ public void testFillExtrusionHeightAsCategoricalSourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("fill-extrusion-height");
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ fillExtrusionHeight(
+ property(
+ "FeaturePropertyA",
+ categorical(
+ stop(1.0f, fillExtrusionHeight(0.3f))
+ )
+ ).withDefaultValue(fillExtrusionHeight(0.3f))
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getFillExtrusionHeight());
+ assertNotNull(layer.getFillExtrusionHeight().getFunction());
+ assertEquals(SourceFunction.class, layer.getFillExtrusionHeight().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getFillExtrusionHeight().getFunction()).getProperty());
+ assertEquals(CategoricalStops.class, layer.getFillExtrusionHeight().getFunction().getStops().getClass());
+ assertNotNull(((SourceFunction) layer.getFillExtrusionHeight().getFunction()).getDefaultValue());
+ assertNotNull(((SourceFunction) layer.getFillExtrusionHeight().getFunction()).getDefaultValue().getValue());
+ assertEquals(0.3f, ((SourceFunction) layer.getFillExtrusionHeight().getFunction()).getDefaultValue().getValue());
+ }
+
+ @Test
+ public void testFillExtrusionHeightAsCompositeFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("fill-extrusion-height");
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ fillExtrusionHeight(
+ composite(
+ "FeaturePropertyA",
+ exponential(
+ stop(0, 0.3f, fillExtrusionHeight(0.9f))
+ ).withBase(0.5f)
+ ).withDefaultValue(fillExtrusionHeight(0.3f))
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getFillExtrusionHeight());
+ assertNotNull(layer.getFillExtrusionHeight().getFunction());
+ assertEquals(CompositeFunction.class, layer.getFillExtrusionHeight().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getFillExtrusionHeight().getFunction()).getProperty());
+ assertEquals(ExponentialStops.class, layer.getFillExtrusionHeight().getFunction().getStops().getClass());
+ assertEquals(1, ((ExponentialStops) layer.getFillExtrusionHeight().getFunction().getStops()).size());
+
+ ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops =
+ (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getFillExtrusionHeight().getFunction().getStops();
+ Stop<Stop.CompositeValue<Float, Float>, Float> stop = stops.iterator().next();
+ assertEquals(0f, stop.in.zoom, 0.001);
+ assertEquals(0.3f, stop.in.value, 0.001f);
+ assertEquals(0.9f, stop.out, 0.001f);
+ }
+
+ @Test
+ public void testFillExtrusionBaseTransition() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("fill-extrusion-baseTransitionOptions");
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setFillExtrusionBaseTransition(options);
+ assertEquals(layer.getFillExtrusionBaseTransition(), options);
+ }
+
+ @Test
+ public void testFillExtrusionBaseAsConstant() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("fill-extrusion-base");
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(fillExtrusionBase(0.3f));
+ assertEquals((Float) layer.getFillExtrusionBase().getValue(), (Float) 0.3f);
+ }
+
+ @Test
+ public void testFillExtrusionBaseAsCameraFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("fill-extrusion-base");
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ fillExtrusionBase(
+ zoom(
+ exponential(
+ stop(2, fillExtrusionBase(0.3f))
+ ).withBase(0.5f)
+ )
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getFillExtrusionBase());
+ assertNotNull(layer.getFillExtrusionBase().getFunction());
+ assertEquals(CameraFunction.class, layer.getFillExtrusionBase().getFunction().getClass());
+ assertEquals(ExponentialStops.class, layer.getFillExtrusionBase().getFunction().getStops().getClass());
+ assertEquals(0.5f, ((ExponentialStops) layer.getFillExtrusionBase().getFunction().getStops()).getBase(), 0.001);
+ assertEquals(1, ((ExponentialStops) layer.getFillExtrusionBase().getFunction().getStops()).size());
+ }
+
+ @Test
+ public void testFillExtrusionBaseAsIdentitySourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("fill-extrusion-base");
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ fillExtrusionBase(property("FeaturePropertyA", Stops.<Float>identity()))
+ );
+
+ // Verify
+ assertNotNull(layer.getFillExtrusionBase());
+ assertNotNull(layer.getFillExtrusionBase().getFunction());
+ assertEquals(SourceFunction.class, layer.getFillExtrusionBase().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getFillExtrusionBase().getFunction()).getProperty());
+ assertEquals(IdentityStops.class, layer.getFillExtrusionBase().getFunction().getStops().getClass());
+ }
+
+ @Test
+ public void testFillExtrusionBaseAsExponentialSourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("fill-extrusion-base");
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ fillExtrusionBase(
+ property(
+ "FeaturePropertyA",
+ exponential(
+ stop(0.3f, fillExtrusionBase(0.3f))
+ ).withBase(0.5f)
+ )
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getFillExtrusionBase());
+ assertNotNull(layer.getFillExtrusionBase().getFunction());
+ assertEquals(SourceFunction.class, layer.getFillExtrusionBase().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getFillExtrusionBase().getFunction()).getProperty());
+ assertEquals(ExponentialStops.class, layer.getFillExtrusionBase().getFunction().getStops().getClass());
+ }
+
+ @Test
+ public void testFillExtrusionBaseAsCategoricalSourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("fill-extrusion-base");
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ fillExtrusionBase(
+ property(
+ "FeaturePropertyA",
+ categorical(
+ stop(1.0f, fillExtrusionBase(0.3f))
+ )
+ ).withDefaultValue(fillExtrusionBase(0.3f))
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getFillExtrusionBase());
+ assertNotNull(layer.getFillExtrusionBase().getFunction());
+ assertEquals(SourceFunction.class, layer.getFillExtrusionBase().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getFillExtrusionBase().getFunction()).getProperty());
+ assertEquals(CategoricalStops.class, layer.getFillExtrusionBase().getFunction().getStops().getClass());
+ assertNotNull(((SourceFunction) layer.getFillExtrusionBase().getFunction()).getDefaultValue());
+ assertNotNull(((SourceFunction) layer.getFillExtrusionBase().getFunction()).getDefaultValue().getValue());
+ assertEquals(0.3f, ((SourceFunction) layer.getFillExtrusionBase().getFunction()).getDefaultValue().getValue());
+ }
+
+ @Test
+ public void testFillExtrusionBaseAsCompositeFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("fill-extrusion-base");
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ fillExtrusionBase(
+ composite(
+ "FeaturePropertyA",
+ exponential(
+ stop(0, 0.3f, fillExtrusionBase(0.9f))
+ ).withBase(0.5f)
+ ).withDefaultValue(fillExtrusionBase(0.3f))
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getFillExtrusionBase());
+ assertNotNull(layer.getFillExtrusionBase().getFunction());
+ assertEquals(CompositeFunction.class, layer.getFillExtrusionBase().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getFillExtrusionBase().getFunction()).getProperty());
+ assertEquals(ExponentialStops.class, layer.getFillExtrusionBase().getFunction().getStops().getClass());
+ assertEquals(1, ((ExponentialStops) layer.getFillExtrusionBase().getFunction().getStops()).size());
+
+ ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops =
+ (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getFillExtrusionBase().getFunction().getStops();
+ Stop<Stop.CompositeValue<Float, Float>, Float> stop = stops.iterator().next();
+ assertEquals(0f, stop.in.zoom, 0.001);
+ assertEquals(0.3f, stop.in.value, 0.001f);
+ assertEquals(0.9f, stop.out, 0.001f);
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
index 2d2a262055..fc10a01356 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
@@ -448,6 +448,17 @@
android:value=".activity.FeatureOverviewActivity"/>
</activity>
<activity
+ android:name=".activity.style.FillExtrusionActivity"
+ android:description="@string/description_fill_extrusion_layer"
+ android:label="@string/activity_fill_extrusion_layer">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_style"/>
+ <meta-data
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value=".activity.FeatureOverviewActivity"/>
+ </activity>
+ <activity
android:name=".activity.style.SymbolLayerActivity"
android:description="@string/description_symbol_layer"
android:label="@string/activity_symbol_layer">
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionActivity.java
new file mode 100644
index 0000000000..9a7790c6e5
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionActivity.java
@@ -0,0 +1,135 @@
+package com.mapbox.mapboxsdk.testapp.activity.style;
+
+import android.graphics.Color;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v7.app.AppCompatActivity;
+
+import com.mapbox.mapboxsdk.camera.CameraPosition;
+import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.style.layers.FillExtrusionLayer;
+import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.services.commons.geojson.Polygon;
+
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionColor;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionHeight;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionOpacity;
+
+/**
+ * Test activity showcasing fill extrusions
+ */
+public class FillExtrusionActivity extends AppCompatActivity {
+
+ private MapView mapView;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_fill_extrusion_layer);
+
+ mapView = (MapView) findViewById(R.id.mapView);
+ mapView.onCreate(savedInstanceState);
+ mapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(@NonNull
+ final MapboxMap map) {
+ Polygon domTower = Polygon.fromCoordinates(new double[][][] {
+ new double[][] {
+ new double[] {
+ 5.12112557888031,
+ 52.09071040847704
+ },
+ new double[] {
+ 5.121227502822875,
+ 52.09053901776669
+ },
+ new double[] {
+ 5.121484994888306,
+ 52.090601641371805
+ },
+ new double[] {
+ 5.1213884353637695,
+ 52.090766439912635
+ },
+ new double[] {
+ 5.12112557888031,
+ 52.09071040847704
+ }
+ }
+ });
+
+ GeoJsonSource source = new GeoJsonSource("extrusion-source", domTower);
+ map.addSource(source);
+
+ map.addLayer(
+ new FillExtrusionLayer("extrusion-layer", source.getId())
+ .withProperties(
+ fillExtrusionHeight(40f),
+ fillExtrusionOpacity(0.5f),
+ fillExtrusionColor(Color.RED)
+ )
+ );
+
+ map.animateCamera(
+ CameraUpdateFactory.newCameraPosition(
+ new CameraPosition.Builder()
+ .target(new LatLng(52.09071040847704, 5.12112557888031))
+ .tilt(45.0)
+ .zoom(18)
+ .build()
+ ),
+ 10000
+ );
+ }
+ });
+ }
+
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ mapView.onStart();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mapView.onResume();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mapView.onPause();
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ mapView.onStop();
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mapView.onLowMemory();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mapView.onDestroy();
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_fill_extrusion_layer.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_fill_extrusion_layer.xml
new file mode 100644
index 0000000000..304841dc69
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_fill_extrusion_layer.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <com.mapbox.mapboxsdk.maps.MapView
+ android:id="@id/mapView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:mapbox_cameraTargetLat="52.090710"
+ app:mapbox_cameraTargetLng="5.121125"
+ app:mapbox_cameraZoom="10"
+ app:mapbox_styleUrl="@string/mapbox_style_mapbox_streets"/>
+
+</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml
index 3df14caf0c..6270eac72d 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml
@@ -57,6 +57,7 @@
<string name="activity_marker_view_rectangle">Marker views in rectangle</string>
<string name="activity_url_transform">Url transform</string>
<string name="activity_restricted_bounds">Restrict camera to a bounds</string>
+ <string name="activity_fill_extrusion_layer">Fill extrusions</string>
<!--Description-->
<string name="description_user_location_tracking">Tracks the location of the user</string>
@@ -113,6 +114,7 @@
<string name="description_circle_layer">Show bus stops and route in Singapore</string>
<string name="description_url_transform">Transform urls on the fly</string>
<string name="description_restricted_bounds">Limit viewport to Iceland</string>
+ <string name="description_fill_extrusion_layer">Shows how to add 3D extruded shapes</string>
<!--Categories-->
<string name="category">category</string>
diff --git a/platform/android/config.cmake b/platform/android/config.cmake
index 7c10ba96f8..c111744ed0 100644
--- a/platform/android/config.cmake
+++ b/platform/android/config.cmake
@@ -130,6 +130,8 @@ add_library(mbgl-android STATIC
platform/android/src/style/layers/circle_layer.hpp
platform/android/src/style/layers/custom_layer.cpp
platform/android/src/style/layers/custom_layer.hpp
+ platform/android/src/style/layers/fill_extrusion_layer.cpp
+ platform/android/src/style/layers/fill_extrusion_layer.hpp
platform/android/src/style/layers/fill_layer.cpp
platform/android/src/style/layers/fill_layer.hpp
platform/android/src/style/layers/layer.cpp
diff --git a/platform/android/scripts/generate-style-code.js b/platform/android/scripts/generate-style-code.js
index 09563f3b9d..5d8fc4cc6d 100644
--- a/platform/android/scripts/generate-style-code.js
+++ b/platform/android/scripts/generate-style-code.js
@@ -34,9 +34,6 @@ var layers = Object.keys(spec.layer.type.values).map((type) => {
};
});
-// XXX Remove fill-extrusion layer for now
-layers = _(layers).filter(layer => layer.type != "fill-extrusion").value();
-
// Process all layer properties
const layoutProperties = _(layers).map('layoutProperties').flatten().value();
const paintProperties = _(layers).map('paintProperties').flatten().value();
diff --git a/platform/android/src/style/layers/fill_extrusion_layer.cpp b/platform/android/src/style/layers/fill_extrusion_layer.cpp
new file mode 100644
index 0000000000..492e1729b9
--- /dev/null
+++ b/platform/android/src/style/layers/fill_extrusion_layer.cpp
@@ -0,0 +1,200 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`.
+
+#include "fill_extrusion_layer.hpp"
+
+#include <string>
+
+#include "../conversion/property_value.hpp"
+#include "../conversion/transition_options.hpp"
+
+namespace mbgl {
+namespace android {
+
+ /**
+ * Creates an owning peer object (for layers not attached to the map) from the JVM side
+ */
+ FillExtrusionLayer::FillExtrusionLayer(jni::JNIEnv& env, jni::String layerId, jni::String sourceId)
+ : Layer(env, std::make_unique<mbgl::style::FillExtrusionLayer>(jni::Make<std::string>(env, layerId), jni::Make<std::string>(env, sourceId))) {
+ }
+
+ /**
+ * Creates a non-owning peer object (for layers currently attached to the map)
+ */
+ FillExtrusionLayer::FillExtrusionLayer(mbgl::Map& map, mbgl::style::FillExtrusionLayer& coreLayer)
+ : Layer(map, coreLayer) {
+ }
+
+ /**
+ * Creates an owning peer object (for layers not attached to the map)
+ */
+ FillExtrusionLayer::FillExtrusionLayer(mbgl::Map& map, std::unique_ptr<mbgl::style::FillExtrusionLayer> coreLayer)
+ : Layer(map, std::move(coreLayer)) {
+ }
+
+ FillExtrusionLayer::~FillExtrusionLayer() = default;
+
+ // Property getters
+
+ jni::Object<jni::ObjectTag> FillExtrusionLayer::getFillExtrusionOpacity(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::FillExtrusionLayer>()->FillExtrusionLayer::getFillExtrusionOpacity());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<TransitionOptions> FillExtrusionLayer::getFillExtrusionOpacityTransition(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ mbgl::style::TransitionOptions options = layer.as<mbgl::style::FillExtrusionLayer>()->FillExtrusionLayer::getFillExtrusionOpacityTransition();
+ return *convert<jni::Object<TransitionOptions>>(env, options);
+ }
+
+ void FillExtrusionLayer::setFillExtrusionOpacityTransition(jni::JNIEnv&, jlong duration, jlong delay) {
+ mbgl::style::TransitionOptions options;
+ options.duration.emplace(mbgl::Milliseconds(duration));
+ options.delay.emplace(mbgl::Milliseconds(delay));
+ layer.as<mbgl::style::FillExtrusionLayer>()->FillExtrusionLayer::setFillExtrusionOpacityTransition(options);
+ }
+
+ jni::Object<jni::ObjectTag> FillExtrusionLayer::getFillExtrusionColor(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::FillExtrusionLayer>()->FillExtrusionLayer::getFillExtrusionColor());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<TransitionOptions> FillExtrusionLayer::getFillExtrusionColorTransition(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ mbgl::style::TransitionOptions options = layer.as<mbgl::style::FillExtrusionLayer>()->FillExtrusionLayer::getFillExtrusionColorTransition();
+ return *convert<jni::Object<TransitionOptions>>(env, options);
+ }
+
+ void FillExtrusionLayer::setFillExtrusionColorTransition(jni::JNIEnv&, jlong duration, jlong delay) {
+ mbgl::style::TransitionOptions options;
+ options.duration.emplace(mbgl::Milliseconds(duration));
+ options.delay.emplace(mbgl::Milliseconds(delay));
+ layer.as<mbgl::style::FillExtrusionLayer>()->FillExtrusionLayer::setFillExtrusionColorTransition(options);
+ }
+
+ jni::Object<jni::ObjectTag> FillExtrusionLayer::getFillExtrusionTranslate(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::FillExtrusionLayer>()->FillExtrusionLayer::getFillExtrusionTranslate());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<TransitionOptions> FillExtrusionLayer::getFillExtrusionTranslateTransition(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ mbgl::style::TransitionOptions options = layer.as<mbgl::style::FillExtrusionLayer>()->FillExtrusionLayer::getFillExtrusionTranslateTransition();
+ return *convert<jni::Object<TransitionOptions>>(env, options);
+ }
+
+ void FillExtrusionLayer::setFillExtrusionTranslateTransition(jni::JNIEnv&, jlong duration, jlong delay) {
+ mbgl::style::TransitionOptions options;
+ options.duration.emplace(mbgl::Milliseconds(duration));
+ options.delay.emplace(mbgl::Milliseconds(delay));
+ layer.as<mbgl::style::FillExtrusionLayer>()->FillExtrusionLayer::setFillExtrusionTranslateTransition(options);
+ }
+
+ jni::Object<jni::ObjectTag> FillExtrusionLayer::getFillExtrusionTranslateAnchor(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::FillExtrusionLayer>()->FillExtrusionLayer::getFillExtrusionTranslateAnchor());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> FillExtrusionLayer::getFillExtrusionPattern(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::FillExtrusionLayer>()->FillExtrusionLayer::getFillExtrusionPattern());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<TransitionOptions> FillExtrusionLayer::getFillExtrusionPatternTransition(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ mbgl::style::TransitionOptions options = layer.as<mbgl::style::FillExtrusionLayer>()->FillExtrusionLayer::getFillExtrusionPatternTransition();
+ return *convert<jni::Object<TransitionOptions>>(env, options);
+ }
+
+ void FillExtrusionLayer::setFillExtrusionPatternTransition(jni::JNIEnv&, jlong duration, jlong delay) {
+ mbgl::style::TransitionOptions options;
+ options.duration.emplace(mbgl::Milliseconds(duration));
+ options.delay.emplace(mbgl::Milliseconds(delay));
+ layer.as<mbgl::style::FillExtrusionLayer>()->FillExtrusionLayer::setFillExtrusionPatternTransition(options);
+ }
+
+ jni::Object<jni::ObjectTag> FillExtrusionLayer::getFillExtrusionHeight(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::FillExtrusionLayer>()->FillExtrusionLayer::getFillExtrusionHeight());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<TransitionOptions> FillExtrusionLayer::getFillExtrusionHeightTransition(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ mbgl::style::TransitionOptions options = layer.as<mbgl::style::FillExtrusionLayer>()->FillExtrusionLayer::getFillExtrusionHeightTransition();
+ return *convert<jni::Object<TransitionOptions>>(env, options);
+ }
+
+ void FillExtrusionLayer::setFillExtrusionHeightTransition(jni::JNIEnv&, jlong duration, jlong delay) {
+ mbgl::style::TransitionOptions options;
+ options.duration.emplace(mbgl::Milliseconds(duration));
+ options.delay.emplace(mbgl::Milliseconds(delay));
+ layer.as<mbgl::style::FillExtrusionLayer>()->FillExtrusionLayer::setFillExtrusionHeightTransition(options);
+ }
+
+ jni::Object<jni::ObjectTag> FillExtrusionLayer::getFillExtrusionBase(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::FillExtrusionLayer>()->FillExtrusionLayer::getFillExtrusionBase());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<TransitionOptions> FillExtrusionLayer::getFillExtrusionBaseTransition(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ mbgl::style::TransitionOptions options = layer.as<mbgl::style::FillExtrusionLayer>()->FillExtrusionLayer::getFillExtrusionBaseTransition();
+ return *convert<jni::Object<TransitionOptions>>(env, options);
+ }
+
+ void FillExtrusionLayer::setFillExtrusionBaseTransition(jni::JNIEnv&, jlong duration, jlong delay) {
+ mbgl::style::TransitionOptions options;
+ options.duration.emplace(mbgl::Milliseconds(duration));
+ options.delay.emplace(mbgl::Milliseconds(delay));
+ layer.as<mbgl::style::FillExtrusionLayer>()->FillExtrusionLayer::setFillExtrusionBaseTransition(options);
+ }
+
+
+ jni::Class<FillExtrusionLayer> FillExtrusionLayer::javaClass;
+
+ jni::jobject* FillExtrusionLayer::createJavaPeer(jni::JNIEnv& env) {
+ static auto constructor = FillExtrusionLayer::javaClass.template GetConstructor<jni::jlong>(env);
+ return FillExtrusionLayer::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this));
+ }
+
+ void FillExtrusionLayer::registerNative(jni::JNIEnv& env) {
+ // Lookup the class
+ FillExtrusionLayer::javaClass = *jni::Class<FillExtrusionLayer>::Find(env).NewGlobalRef(env).release();
+
+ #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
+
+ // Register the peer
+ jni::RegisterNativePeer<FillExtrusionLayer>(
+ env, FillExtrusionLayer::javaClass, "nativePtr",
+ std::make_unique<FillExtrusionLayer, JNIEnv&, jni::String, jni::String>,
+ "initialize",
+ "finalize",
+ METHOD(&FillExtrusionLayer::getFillExtrusionOpacityTransition, "nativeGetFillExtrusionOpacityTransition"),
+ METHOD(&FillExtrusionLayer::setFillExtrusionOpacityTransition, "nativeSetFillExtrusionOpacityTransition"),
+ METHOD(&FillExtrusionLayer::getFillExtrusionOpacity, "nativeGetFillExtrusionOpacity"),
+ METHOD(&FillExtrusionLayer::getFillExtrusionColorTransition, "nativeGetFillExtrusionColorTransition"),
+ METHOD(&FillExtrusionLayer::setFillExtrusionColorTransition, "nativeSetFillExtrusionColorTransition"),
+ METHOD(&FillExtrusionLayer::getFillExtrusionColor, "nativeGetFillExtrusionColor"),
+ METHOD(&FillExtrusionLayer::getFillExtrusionTranslateTransition, "nativeGetFillExtrusionTranslateTransition"),
+ METHOD(&FillExtrusionLayer::setFillExtrusionTranslateTransition, "nativeSetFillExtrusionTranslateTransition"),
+ METHOD(&FillExtrusionLayer::getFillExtrusionTranslate, "nativeGetFillExtrusionTranslate"),
+ METHOD(&FillExtrusionLayer::getFillExtrusionTranslateAnchor, "nativeGetFillExtrusionTranslateAnchor"),
+ METHOD(&FillExtrusionLayer::getFillExtrusionPatternTransition, "nativeGetFillExtrusionPatternTransition"),
+ METHOD(&FillExtrusionLayer::setFillExtrusionPatternTransition, "nativeSetFillExtrusionPatternTransition"),
+ METHOD(&FillExtrusionLayer::getFillExtrusionPattern, "nativeGetFillExtrusionPattern"),
+ METHOD(&FillExtrusionLayer::getFillExtrusionHeightTransition, "nativeGetFillExtrusionHeightTransition"),
+ METHOD(&FillExtrusionLayer::setFillExtrusionHeightTransition, "nativeSetFillExtrusionHeightTransition"),
+ METHOD(&FillExtrusionLayer::getFillExtrusionHeight, "nativeGetFillExtrusionHeight"),
+ METHOD(&FillExtrusionLayer::getFillExtrusionBaseTransition, "nativeGetFillExtrusionBaseTransition"),
+ METHOD(&FillExtrusionLayer::setFillExtrusionBaseTransition, "nativeSetFillExtrusionBaseTransition"),
+ METHOD(&FillExtrusionLayer::getFillExtrusionBase, "nativeGetFillExtrusionBase"));
+ }
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/layers/fill_extrusion_layer.hpp b/platform/android/src/style/layers/fill_extrusion_layer.hpp
new file mode 100644
index 0000000000..11a74bc8ef
--- /dev/null
+++ b/platform/android/src/style/layers/fill_extrusion_layer.hpp
@@ -0,0 +1,62 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`.
+
+#pragma once
+
+#include "layer.hpp"
+#include "../transition_options.hpp"
+#include <mbgl/style/layers/fill_extrusion_layer.hpp>
+#include <jni/jni.hpp>
+
+namespace mbgl {
+namespace android {
+
+class FillExtrusionLayer : public Layer {
+public:
+
+ static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/layers/FillExtrusionLayer"; };
+
+ static jni::Class<FillExtrusionLayer> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+
+ FillExtrusionLayer(jni::JNIEnv&, jni::String, jni::String);
+
+ FillExtrusionLayer(mbgl::Map&, mbgl::style::FillExtrusionLayer&);
+
+ FillExtrusionLayer(mbgl::Map&, std::unique_ptr<mbgl::style::FillExtrusionLayer>);
+
+ ~FillExtrusionLayer();
+
+ // Properties
+
+ jni::Object<jni::ObjectTag> getFillExtrusionOpacity(jni::JNIEnv&);
+ void setFillExtrusionOpacityTransition(jni::JNIEnv&, jlong duration, jlong delay);
+ jni::Object<TransitionOptions> getFillExtrusionOpacityTransition(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getFillExtrusionColor(jni::JNIEnv&);
+ void setFillExtrusionColorTransition(jni::JNIEnv&, jlong duration, jlong delay);
+ jni::Object<TransitionOptions> getFillExtrusionColorTransition(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getFillExtrusionTranslate(jni::JNIEnv&);
+ void setFillExtrusionTranslateTransition(jni::JNIEnv&, jlong duration, jlong delay);
+ jni::Object<TransitionOptions> getFillExtrusionTranslateTransition(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getFillExtrusionTranslateAnchor(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getFillExtrusionPattern(jni::JNIEnv&);
+ void setFillExtrusionPatternTransition(jni::JNIEnv&, jlong duration, jlong delay);
+ jni::Object<TransitionOptions> getFillExtrusionPatternTransition(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getFillExtrusionHeight(jni::JNIEnv&);
+ void setFillExtrusionHeightTransition(jni::JNIEnv&, jlong duration, jlong delay);
+ jni::Object<TransitionOptions> getFillExtrusionHeightTransition(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getFillExtrusionBase(jni::JNIEnv&);
+ void setFillExtrusionBaseTransition(jni::JNIEnv&, jlong duration, jlong delay);
+ jni::Object<TransitionOptions> getFillExtrusionBaseTransition(jni::JNIEnv&);
+ jni::jobject* createJavaPeer(jni::JNIEnv&);
+
+}; // class FillExtrusionLayer
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/layers/layer.cpp b/platform/android/src/style/layers/layer.cpp
index 58c0c5ee84..1ebad19a87 100644
--- a/platform/android/src/style/layers/layer.cpp
+++ b/platform/android/src/style/layers/layer.cpp
@@ -139,6 +139,8 @@ namespace android {
layer.as<SymbolLayer>()->setSourceLayer(layerId);
} else if (layer.is<CircleLayer>()) {
layer.as<CircleLayer>()->setSourceLayer(layerId);
+ } else if(layer.is<FillExtrusionLayer>()) {
+ layer.as<FillExtrusionLayer>()->setSourceLayer(layerId);
} else {
mbgl::Log::Warning(mbgl::Event::JNI, "Layer doesn't support source layer");
}
@@ -156,6 +158,8 @@ namespace android {
sourceLayerId = layer.as<SymbolLayer>()->getSourceLayer();
} else if (layer.is<CircleLayer>()) {
sourceLayerId = layer.as<CircleLayer>()->getSourceLayer();
+ } else if (layer.is<FillExtrusionLayer>()) {
+ sourceLayerId = layer.as<FillExtrusionLayer>()->getSourceLayer();
} else {
mbgl::Log::Warning(mbgl::Event::JNI, "Layer doesn't support source layer");
}
diff --git a/platform/android/src/style/layers/layers.cpp b/platform/android/src/style/layers/layers.cpp
index 5c6ee1ae8f..5c49f875ee 100644
--- a/platform/android/src/style/layers/layers.cpp
+++ b/platform/android/src/style/layers/layers.cpp
@@ -3,6 +3,7 @@
#include <mbgl/style/layer.hpp>
#include <mbgl/style/layers/background_layer.hpp>
#include <mbgl/style/layers/circle_layer.hpp>
+#include <mbgl/style/layers/fill_extrusion_layer.hpp>
#include <mbgl/style/layers/fill_layer.hpp>
#include <mbgl/style/layers/line_layer.hpp>
#include <mbgl/style/layers/raster_layer.hpp>
@@ -12,11 +13,13 @@
#include "background_layer.hpp"
#include "circle_layer.hpp"
#include "custom_layer.hpp"
+#include "fill_extrusion_layer.hpp"
#include "fill_layer.hpp"
#include "line_layer.hpp"
#include "raster_layer.hpp"
#include "symbol_layer.hpp"
#include "unknown_layer.hpp"
+#include "fill_extrusion_layer.hpp"
namespace mbgl {
namespace android {
@@ -26,6 +29,8 @@ static Layer* initializeLayerPeer(mbgl::Map& map, mbgl::style::Layer& coreLayer)
return new BackgroundLayer(map, *coreLayer.as<mbgl::style::BackgroundLayer>());
} else if (coreLayer.is<mbgl::style::CircleLayer>()) {
return new CircleLayer(map, *coreLayer.as<mbgl::style::CircleLayer>());
+ } else if (coreLayer.is<mbgl::style::FillExtrusionLayer>()) {
+ return new FillExtrusionLayer(map, *coreLayer.as<mbgl::style::FillExtrusionLayer>());
} else if (coreLayer.is<mbgl::style::FillLayer>()) {
return new FillLayer(map, *coreLayer.as<mbgl::style::FillLayer>());
} else if (coreLayer.is<mbgl::style::LineLayer>()) {
@@ -51,6 +56,8 @@ static Layer* initializeLayerPeer(Map& map, std::unique_ptr<mbgl::style::Layer>
return createPeer<style::BackgroundLayer, BackgroundLayer>(map, std::move(coreLayer));
} else if (coreLayer->is<style::CircleLayer>()) {
return createPeer<style::CircleLayer, CircleLayer>(map, std::move(coreLayer));
+ } else if (coreLayer->is<style::FillExtrusionLayer>()) {
+ return createPeer<style::FillExtrusionLayer, FillExtrusionLayer>(map, std::move(coreLayer));
} else if (coreLayer->is<style::FillLayer>()) {
return createPeer<style::FillLayer, FillLayer>(map, std::move(coreLayer));
} else if (coreLayer->is<style::LineLayer>()) {
@@ -85,6 +92,7 @@ void registerNativeLayers(jni::JNIEnv& env) {
BackgroundLayer::registerNative(env);
CircleLayer::registerNative(env);
CustomLayer::registerNative(env);
+ FillExtrusionLayer::registerNative(env);
FillLayer::registerNative(env);
LineLayer::registerNative(env);
RasterLayer::registerNative(env);
diff --git a/platform/darwin/docs/guides/For Style Authors.md.ejs b/platform/darwin/docs/guides/For Style Authors.md.ejs
index 86f8c46f53..2d9ce635b8 100644
--- a/platform/darwin/docs/guides/For Style Authors.md.ejs
+++ b/platform/darwin/docs/guides/For Style Authors.md.ejs
@@ -240,7 +240,7 @@ whose names differ from the style specification are listed below:
<% for (const type in renamedProperties) { -%>
<% if (renamedProperties.hasOwnProperty(type)) { -%>
-### <%- camelize(type) %> style layers
+### <%- camelize(unhyphenate(type)) %> style layers
In style JSON | In Objective-C | In Swift
--------------|----------------|---------
diff --git a/platform/darwin/scripts/generate-style-code.js b/platform/darwin/scripts/generate-style-code.js
index 7e798154e4..5d5adbbef6 100644
--- a/platform/darwin/scripts/generate-style-code.js
+++ b/platform/darwin/scripts/generate-style-code.js
@@ -13,11 +13,6 @@ const suffix = 'StyleLayer';
let spec = _.merge(require('../../../mapbox-gl-js/src/style-spec/reference/v8'), require('./style-spec-overrides-v8.json'));
-///
-// Temporarily IGNORE layers that are in the spec yet still not supported in mbgl core
-///
-delete spec.layer.type.values['fill-extrusion'];
-
// Rename properties and keep `original` for use with setters and getters
_.forOwn(cocoaConventions, function (properties, kind) {
_.forOwn(properties, function (newName, oldName) {
diff --git a/platform/darwin/scripts/style-spec-cocoa-conventions-v8.json b/platform/darwin/scripts/style-spec-cocoa-conventions-v8.json
index 9d80ff3896..830b6d69f9 100644
--- a/platform/darwin/scripts/style-spec-cocoa-conventions-v8.json
+++ b/platform/darwin/scripts/style-spec-cocoa-conventions-v8.json
@@ -30,6 +30,10 @@
"fill-translate": "fill-translation",
"fill-translate-anchor": "fill-translation-anchor"
},
+ "paint_fill-extrusion": {
+ "fill-extrusion-translate": "fill-extrusion-translation",
+ "fill-extrusion-translate-anchor": "fill-extrusion-translation-anchor"
+ },
"paint_raster": {
"raster-brightness-min": "minimum-raster-brightness",
"raster-brightness-max": "maximum-raster-brightness",
diff --git a/platform/darwin/scripts/style-spec-overrides-v8.json b/platform/darwin/scripts/style-spec-overrides-v8.json
index 28740d458b..a594ef2957 100644
--- a/platform/darwin/scripts/style-spec-overrides-v8.json
+++ b/platform/darwin/scripts/style-spec-overrides-v8.json
@@ -5,6 +5,9 @@
"fill": {
"doc": "An `MGLFillStyleLayer` is a style layer that renders one or more filled (and optionally stroked) polygons on the map.\n\nUse a fill style layer to configure the visual appearance of polygon or multipolygon features in vector tiles loaded by an `MGLVectorSource` object or `MGLPolygon`, `MGLPolygonFeature`, `MGLMultiPolygon`, or `MGLMultiPolygonFeature` instances in an `MGLShapeSource` object."
},
+ "fill-extrusion": {
+ "doc": "An `MGLFillExtrusionStyleLayer` is a style layer that renders one or more 3D extruded polygons on the map.\n\nUse a fill-extrusion style layer to configure the visual appearance of polygon or multipolygon features in vector tiles loaded by an `MGLVectorSource` object or `MGLPolygon`, `MGLPolygonFeature`, `MGLMultiPolygon`, or `MGLMultiPolygonFeature` instances in an `MGLShapeSource` object."
+ },
"line": {
"doc": "An `MGLLineStyleLayer` is a style layer that renders one or more stroked polylines on the map.\n\nUse a line style layer to configure the visual appearance of polyline or multipolyline features in vector tiles loaded by an `MGLVectorSource` object or `MGLPolyline`, `MGLPolylineFeature`, `MGLMultiPolyline`, or `MGLMultiPolylineFeature` instances in an `MGLShapeSource` object."
},
@@ -50,6 +53,17 @@
"doc": "Name of image in style images to use for drawing image fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512)."
}
},
+ "paint_fill-extrusion": {
+ "fill-extrusion-pattern": {
+ "doc": "Name of image in style images to use for drawing image fill-extrusions. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512)."
+ },
+ "fill-extrusion-translate": {
+ "doc": "The geometry's offset."
+ },
+ "fill-extrusion-color": {
+ "doc": "The base color of this layer. The extrusion's surfaces will be shaded differently based on this color in combination with the `light` settings. If this color is specified with an alpha component, the alpha component will be ignored; use `fill-extrusion-opacity` to set layer opacityco."
+ }
+ },
"paint_line": {
"line-pattern": {
"doc": "Name of image in style images to use for drawing image lines. For seamless patterns, image width must be a factor of two (2, 4, 8, ..., 512)."
diff --git a/platform/darwin/src/MGLFillExtrusionStyleLayer.h b/platform/darwin/src/MGLFillExtrusionStyleLayer.h
new file mode 100644
index 0000000000..84f6bedde4
--- /dev/null
+++ b/platform/darwin/src/MGLFillExtrusionStyleLayer.h
@@ -0,0 +1,364 @@
+// This file is generated.
+// Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
+
+#import "MGLFoundation.h"
+#import "MGLStyleValue.h"
+#import "MGLVectorStyleLayer.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ Controls the translation reference point.
+
+ Values of this type are used in the `MGLFillExtrusionStyleLayer.fillExtrusionTranslationAnchor`
+ property.
+ */
+typedef NS_ENUM(NSUInteger, MGLFillExtrusionTranslationAnchor) {
+ /**
+ The fill extrusion is translated relative to the map.
+ */
+ MGLFillExtrusionTranslationAnchorMap,
+ /**
+ The fill extrusion is translated relative to the viewport.
+ */
+ MGLFillExtrusionTranslationAnchorViewport,
+};
+
+/**
+ An `MGLFillExtrusionStyleLayer` is a style layer that renders one or more 3D
+ extruded polygons on the map.
+
+ Use a fill-extrusion style layer to configure the visual appearance of polygon
+ or multipolygon features in vector tiles loaded by an `MGLVectorSource` object
+ or `MGLPolygon`, `MGLPolygonFeature`, `MGLMultiPolygon`, or
+ `MGLMultiPolygonFeature` instances in an `MGLShapeSource` object.
+
+ You can access an existing fill-extrusion style layer using the
+ `-[MGLStyle layerWithIdentifier:]` method if you know its identifier;
+ otherwise, find it using the `MGLStyle.layers` property. You can also create a
+ new fill-extrusion style layer and add it to the style using a method such as
+ `-[MGLStyle addLayer:]`.
+
+ ### Example
+
+ ```swift
+ let layer = MGLFillExtrusionStyleLayer(identifier: "buildings", source: buildings)
+ layer.sourceLayerIdentifier = "building"
+ layer.fillExtrusionHeight = MGLStyleValue(interpolationMode: .identity, sourceStops: nil, attributeName: "height", options: nil)
+ layer.fillExtrusionBase = MGLStyleValue(interpolationMode: .identity, sourceStops: nil, attributeName: "min_height", options: nil)
+ layer.predicate = NSPredicate(format: "extrude == TRUE")
+ mapView.style?.addLayer(layer)
+ ```
+ */
+MGL_EXPORT
+@interface MGLFillExtrusionStyleLayer : MGLVectorStyleLayer
+
+/**
+ Returns a fill-extrusion style layer initialized with an identifier and source.
+
+ After initializing and configuring the style layer, add it to a map view’s
+ style using the `-[MGLStyle addLayer:]` or
+ `-[MGLStyle insertLayer:belowLayer:]` method.
+
+ @param identifier A string that uniquely identifies the source in the style to
+ which it is added.
+ @param source The source from which to obtain the data to style. If the source
+ has not yet been added to the current style, the behavior is undefined.
+ @return An initialized foreground style layer.
+ */
+- (instancetype)initWithIdentifier:(NSString *)identifier source:(MGLSource *)source;
+
+#pragma mark - Accessing the Paint Attributes
+
+/**
+ The height with which to extrude the base of this layer. Must be less than or
+ equal to `fillExtrusionHeight`.
+
+ This property is measured in meters.
+
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `0`. Set this property to `nil` to reset
+ it to the default value.
+
+ This property is only applied to the style if `fillExtrusionHeight` is
+ non-`nil`. Otherwise, it is ignored.
+
+ You can set this property to an instance of:
+
+ * `MGLConstantStyleValue`
+ * `MGLCameraStyleFunction` with an interpolation mode of:
+ * `MGLInterpolationModeExponential`
+ * `MGLInterpolationModeInterval`
+ * `MGLSourceStyleFunction` with an interpolation mode of:
+ * `MGLInterpolationModeExponential`
+ * `MGLInterpolationModeInterval`
+ * `MGLInterpolationModeCategorical`
+ * `MGLInterpolationModeIdentity`
+ * `MGLCompositeStyleFunction` with an interpolation mode of:
+ * `MGLInterpolationModeExponential`
+ * `MGLInterpolationModeInterval`
+ * `MGLInterpolationModeCategorical`
+ */
+@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *fillExtrusionBase;
+
+/**
+ The transition affecting any changes to this layer’s `fillExtrusionBase` property.
+
+ This property corresponds to the `fill-extrusion-base-transition` property in the style JSON file format.
+*/
+@property (nonatomic) MGLTransition fillExtrusionBaseTransition;
+
+#if TARGET_OS_IPHONE
+/**
+ The base color of this layer. The extrusion's surfaces will be shaded
+ differently based on this color in combination with the `light` settings. If
+ this color is specified with an alpha component, the alpha component will be
+ ignored; use `fillExtrusionOpacity` to set layer opacityco.
+
+ The default value of this property is an `MGLStyleValue` object containing
+ `UIColor.blackColor`. Set this property to `nil` to reset it to the default
+ value.
+
+ This property is only applied to the style if `fillExtrusionPattern` is set to
+ `nil`. Otherwise, it is ignored.
+
+ You can set this property to an instance of:
+
+ * `MGLConstantStyleValue`
+ * `MGLCameraStyleFunction` with an interpolation mode of:
+ * `MGLInterpolationModeExponential`
+ * `MGLInterpolationModeInterval`
+ * `MGLSourceStyleFunction` with an interpolation mode of:
+ * `MGLInterpolationModeExponential`
+ * `MGLInterpolationModeInterval`
+ * `MGLInterpolationModeCategorical`
+ * `MGLInterpolationModeIdentity`
+ * `MGLCompositeStyleFunction` with an interpolation mode of:
+ * `MGLInterpolationModeExponential`
+ * `MGLInterpolationModeInterval`
+ * `MGLInterpolationModeCategorical`
+ */
+@property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *fillExtrusionColor;
+#else
+/**
+ The base color of this layer. The extrusion's surfaces will be shaded
+ differently based on this color in combination with the `light` settings. If
+ this color is specified with an alpha component, the alpha component will be
+ ignored; use `fillExtrusionOpacity` to set layer opacityco.
+
+ The default value of this property is an `MGLStyleValue` object containing
+ `NSColor.blackColor`. Set this property to `nil` to reset it to the default
+ value.
+
+ This property is only applied to the style if `fillExtrusionPattern` is set to
+ `nil`. Otherwise, it is ignored.
+
+ You can set this property to an instance of:
+
+ * `MGLConstantStyleValue`
+ * `MGLCameraStyleFunction` with an interpolation mode of:
+ * `MGLInterpolationModeExponential`
+ * `MGLInterpolationModeInterval`
+ * `MGLSourceStyleFunction` with an interpolation mode of:
+ * `MGLInterpolationModeExponential`
+ * `MGLInterpolationModeInterval`
+ * `MGLInterpolationModeCategorical`
+ * `MGLInterpolationModeIdentity`
+ * `MGLCompositeStyleFunction` with an interpolation mode of:
+ * `MGLInterpolationModeExponential`
+ * `MGLInterpolationModeInterval`
+ * `MGLInterpolationModeCategorical`
+ */
+@property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *fillExtrusionColor;
+#endif
+
+/**
+ The transition affecting any changes to this layer’s `fillExtrusionColor` property.
+
+ This property corresponds to the `fill-extrusion-color-transition` property in the style JSON file format.
+*/
+@property (nonatomic) MGLTransition fillExtrusionColorTransition;
+
+/**
+ The height with which to extrude this layer.
+
+ This property is measured in meters.
+
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `0`. Set this property to `nil` to reset
+ it to the default value.
+
+ You can set this property to an instance of:
+
+ * `MGLConstantStyleValue`
+ * `MGLCameraStyleFunction` with an interpolation mode of:
+ * `MGLInterpolationModeExponential`
+ * `MGLInterpolationModeInterval`
+ * `MGLSourceStyleFunction` with an interpolation mode of:
+ * `MGLInterpolationModeExponential`
+ * `MGLInterpolationModeInterval`
+ * `MGLInterpolationModeCategorical`
+ * `MGLInterpolationModeIdentity`
+ * `MGLCompositeStyleFunction` with an interpolation mode of:
+ * `MGLInterpolationModeExponential`
+ * `MGLInterpolationModeInterval`
+ * `MGLInterpolationModeCategorical`
+ */
+@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *fillExtrusionHeight;
+
+/**
+ The transition affecting any changes to this layer’s `fillExtrusionHeight` property.
+
+ This property corresponds to the `fill-extrusion-height-transition` property in the style JSON file format.
+*/
+@property (nonatomic) MGLTransition fillExtrusionHeightTransition;
+
+/**
+ The opacity of the entire fill extrusion layer. This is rendered on a
+ per-layer, not per-feature, basis, and data-driven styling is not available.
+
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSNumber` object containing the float `1`. Set this property to `nil` to reset
+ it to the default value.
+
+ You can set this property to an instance of:
+
+ * `MGLConstantStyleValue`
+ * `MGLCameraStyleFunction` with an interpolation mode of:
+ * `MGLInterpolationModeExponential`
+ * `MGLInterpolationModeInterval`
+ */
+@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *fillExtrusionOpacity;
+
+/**
+ The transition affecting any changes to this layer’s `fillExtrusionOpacity` property.
+
+ This property corresponds to the `fill-extrusion-opacity-transition` property in the style JSON file format.
+*/
+@property (nonatomic) MGLTransition fillExtrusionOpacityTransition;
+
+/**
+ Name of image in style images to use for drawing image fill-extrusions. For
+ seamless patterns, image width and height must be a factor of two (2, 4, 8,
+ ..., 512).
+
+ You can set this property to an instance of:
+
+ * `MGLConstantStyleValue`
+ * `MGLCameraStyleFunction` with an interpolation mode of
+ `MGLInterpolationModeInterval`
+ */
+@property (nonatomic, null_resettable) MGLStyleValue<NSString *> *fillExtrusionPattern;
+
+/**
+ The transition affecting any changes to this layer’s `fillExtrusionPattern` property.
+
+ This property corresponds to the `fill-extrusion-pattern-transition` property in the style JSON file format.
+*/
+@property (nonatomic) MGLTransition fillExtrusionPatternTransition;
+
+#if TARGET_OS_IPHONE
+/**
+ The geometry's offset.
+
+ This property is measured in points.
+
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSValue` object containing a `CGVector` struct set to 0 points rightward and 0
+ points downward. Set this property to `nil` to reset it to the default value.
+
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-fill-extrusion-translate"><code>fill-extrusion-translate</code></a>
+ layout property in the Mapbox Style Specification.
+
+ You can set this property to an instance of:
+
+ * `MGLConstantStyleValue`
+ * `MGLCameraStyleFunction` with an interpolation mode of:
+ * `MGLInterpolationModeExponential`
+ * `MGLInterpolationModeInterval`
+ */
+@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *fillExtrusionTranslation;
+#else
+/**
+ The geometry's offset.
+
+ This property is measured in points.
+
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSValue` object containing a `CGVector` struct set to 0 points rightward and 0
+ points upward. Set this property to `nil` to reset it to the default value.
+
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-fill-extrusion-translate"><code>fill-extrusion-translate</code></a>
+ layout property in the Mapbox Style Specification.
+
+ You can set this property to an instance of:
+
+ * `MGLConstantStyleValue`
+ * `MGLCameraStyleFunction` with an interpolation mode of:
+ * `MGLInterpolationModeExponential`
+ * `MGLInterpolationModeInterval`
+ */
+@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *fillExtrusionTranslation;
+#endif
+
+/**
+ The transition affecting any changes to this layer’s `fillExtrusionTranslation` property.
+
+ This property corresponds to the `fill-extrusion-translate-transition` property in the style JSON file format.
+*/
+@property (nonatomic) MGLTransition fillExtrusionTranslationTransition;
+
+@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *fillExtrusionTranslate __attribute__((unavailable("Use fillExtrusionTranslation instead.")));
+
+/**
+ Controls the translation reference point.
+
+ The default value of this property is an `MGLStyleValue` object containing an
+ `NSValue` object containing `MGLFillExtrusionTranslationAnchorMap`. Set this
+ property to `nil` to reset it to the default value.
+
+ This property is only applied to the style if `fillExtrusionTranslation` is
+ non-`nil`. Otherwise, it is ignored.
+
+ This attribute corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-style-spec/#paint-fill-extrusion-translate-anchor"><code>fill-extrusion-translate-anchor</code></a>
+ layout property in the Mapbox Style Specification.
+
+ You can set this property to an instance of:
+
+ * `MGLConstantStyleValue`
+ * `MGLCameraStyleFunction` with an interpolation mode of
+ `MGLInterpolationModeInterval`
+ */
+@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *fillExtrusionTranslationAnchor;
+
+@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *fillExtrusionTranslateAnchor __attribute__((unavailable("Use fillExtrusionTranslationAnchor instead.")));
+
+@end
+
+/**
+ Methods for wrapping an enumeration value for a style layer attribute in an
+ `MGLFillExtrusionStyleLayer` object and unwrapping its raw value.
+ */
+@interface NSValue (MGLFillExtrusionStyleLayerAdditions)
+
+#pragma mark Working with Fill extrusion Style Layer Attribute Values
+
+/**
+ Creates a new value object containing the given `MGLFillExtrusionTranslationAnchor` enumeration.
+
+ @param fillExtrusionTranslationAnchor The value for the new object.
+ @return A new value object that contains the enumeration value.
+ */
++ (instancetype)valueWithMGLFillExtrusionTranslationAnchor:(MGLFillExtrusionTranslationAnchor)fillExtrusionTranslationAnchor;
+
+/**
+ The `MGLFillExtrusionTranslationAnchor` enumeration representation of the value.
+ */
+@property (readonly) MGLFillExtrusionTranslationAnchor MGLFillExtrusionTranslationAnchorValue;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLFillExtrusionStyleLayer.mm b/platform/darwin/src/MGLFillExtrusionStyleLayer.mm
new file mode 100644
index 0000000000..b00ed8e09f
--- /dev/null
+++ b/platform/darwin/src/MGLFillExtrusionStyleLayer.mm
@@ -0,0 +1,335 @@
+// This file is generated.
+// Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
+
+#import "MGLSource.h"
+#import "NSPredicate+MGLAdditions.h"
+#import "NSDate+MGLAdditions.h"
+#import "MGLStyleLayer_Private.h"
+#import "MGLStyleValue_Private.h"
+#import "MGLFillExtrusionStyleLayer.h"
+
+#include <mbgl/style/transition_options.hpp>
+#include <mbgl/style/layers/fill_extrusion_layer.hpp>
+
+namespace mbgl {
+
+ MBGL_DEFINE_ENUM(MGLFillExtrusionTranslationAnchor, {
+ { MGLFillExtrusionTranslationAnchorMap, "map" },
+ { MGLFillExtrusionTranslationAnchorViewport, "viewport" },
+ });
+
+}
+
+@interface MGLFillExtrusionStyleLayer ()
+
+@property (nonatomic, readonly) mbgl::style::FillExtrusionLayer *rawLayer;
+
+@end
+
+@implementation MGLFillExtrusionStyleLayer
+
+- (instancetype)initWithIdentifier:(NSString *)identifier source:(MGLSource *)source
+{
+ auto layer = std::make_unique<mbgl::style::FillExtrusionLayer>(identifier.UTF8String, source.identifier.UTF8String);
+ return self = [super initWithPendingLayer:std::move(layer)];
+}
+
+- (mbgl::style::FillExtrusionLayer *)rawLayer
+{
+ return (mbgl::style::FillExtrusionLayer *)super.rawLayer;
+}
+
+- (NSString *)sourceIdentifier
+{
+ MGLAssertStyleLayerIsValid();
+
+ return @(self.rawLayer->getSourceID().c_str());
+}
+
+- (NSString *)sourceLayerIdentifier
+{
+ MGLAssertStyleLayerIsValid();
+
+ auto layerID = self.rawLayer->getSourceLayer();
+ return layerID.empty() ? nil : @(layerID.c_str());
+}
+
+- (void)setSourceLayerIdentifier:(NSString *)sourceLayerIdentifier
+{
+ MGLAssertStyleLayerIsValid();
+
+ self.rawLayer->setSourceLayer(sourceLayerIdentifier.UTF8String ?: "");
+}
+
+- (void)setPredicate:(NSPredicate *)predicate
+{
+ MGLAssertStyleLayerIsValid();
+
+ self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::NullFilter());
+}
+
+- (NSPredicate *)predicate
+{
+ MGLAssertStyleLayerIsValid();
+
+ return [NSPredicate mgl_predicateWithFilter:self.rawLayer->getFilter()];
+}
+
+#pragma mark - Accessing the Paint Attributes
+
+- (void)setFillExtrusionBase:(MGLStyleValue<NSNumber *> *)fillExtrusionBase {
+ MGLAssertStyleLayerIsValid();
+
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(fillExtrusionBase);
+ self.rawLayer->setFillExtrusionBase(mbglValue);
+}
+
+- (MGLStyleValue<NSNumber *> *)fillExtrusionBase {
+ MGLAssertStyleLayerIsValid();
+
+ auto propertyValue = self.rawLayer->getFillExtrusionBase();
+ if (propertyValue.isUndefined()) {
+ return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultFillExtrusionBase());
+ }
+ return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue);
+}
+
+- (void)setFillExtrusionBaseTransition:(MGLTransition )transition {
+ MGLAssertStyleLayerIsValid();
+
+ mbgl::style::TransitionOptions options { { MGLDurationFromTimeInterval(transition.duration) }, { MGLDurationFromTimeInterval(transition.delay) } };
+ self.rawLayer->setFillExtrusionBaseTransition(options);
+}
+
+- (MGLTransition)fillExtrusionBaseTransition {
+ MGLAssertStyleLayerIsValid();
+
+ mbgl::style::TransitionOptions transitionOptions = self.rawLayer->getFillExtrusionBaseTransition();
+ MGLTransition transition;
+ transition.duration = MGLTimeIntervalFromDuration(transitionOptions.duration.value_or(mbgl::Duration::zero()));
+ transition.delay = MGLTimeIntervalFromDuration(transitionOptions.delay.value_or(mbgl::Duration::zero()));
+
+ return transition;
+}
+
+- (void)setFillExtrusionColor:(MGLStyleValue<MGLColor *> *)fillExtrusionColor {
+ MGLAssertStyleLayerIsValid();
+
+ auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenPropertyValue(fillExtrusionColor);
+ self.rawLayer->setFillExtrusionColor(mbglValue);
+}
+
+- (MGLStyleValue<MGLColor *> *)fillExtrusionColor {
+ MGLAssertStyleLayerIsValid();
+
+ auto propertyValue = self.rawLayer->getFillExtrusionColor();
+ if (propertyValue.isUndefined()) {
+ return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(self.rawLayer->getDefaultFillExtrusionColor());
+ }
+ return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(propertyValue);
+}
+
+- (void)setFillExtrusionColorTransition:(MGLTransition )transition {
+ MGLAssertStyleLayerIsValid();
+
+ mbgl::style::TransitionOptions options { { MGLDurationFromTimeInterval(transition.duration) }, { MGLDurationFromTimeInterval(transition.delay) } };
+ self.rawLayer->setFillExtrusionColorTransition(options);
+}
+
+- (MGLTransition)fillExtrusionColorTransition {
+ MGLAssertStyleLayerIsValid();
+
+ mbgl::style::TransitionOptions transitionOptions = self.rawLayer->getFillExtrusionColorTransition();
+ MGLTransition transition;
+ transition.duration = MGLTimeIntervalFromDuration(transitionOptions.duration.value_or(mbgl::Duration::zero()));
+ transition.delay = MGLTimeIntervalFromDuration(transitionOptions.delay.value_or(mbgl::Duration::zero()));
+
+ return transition;
+}
+
+- (void)setFillExtrusionHeight:(MGLStyleValue<NSNumber *> *)fillExtrusionHeight {
+ MGLAssertStyleLayerIsValid();
+
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(fillExtrusionHeight);
+ self.rawLayer->setFillExtrusionHeight(mbglValue);
+}
+
+- (MGLStyleValue<NSNumber *> *)fillExtrusionHeight {
+ MGLAssertStyleLayerIsValid();
+
+ auto propertyValue = self.rawLayer->getFillExtrusionHeight();
+ if (propertyValue.isUndefined()) {
+ return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultFillExtrusionHeight());
+ }
+ return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue);
+}
+
+- (void)setFillExtrusionHeightTransition:(MGLTransition )transition {
+ MGLAssertStyleLayerIsValid();
+
+ mbgl::style::TransitionOptions options { { MGLDurationFromTimeInterval(transition.duration) }, { MGLDurationFromTimeInterval(transition.delay) } };
+ self.rawLayer->setFillExtrusionHeightTransition(options);
+}
+
+- (MGLTransition)fillExtrusionHeightTransition {
+ MGLAssertStyleLayerIsValid();
+
+ mbgl::style::TransitionOptions transitionOptions = self.rawLayer->getFillExtrusionHeightTransition();
+ MGLTransition transition;
+ transition.duration = MGLTimeIntervalFromDuration(transitionOptions.duration.value_or(mbgl::Duration::zero()));
+ transition.delay = MGLTimeIntervalFromDuration(transitionOptions.delay.value_or(mbgl::Duration::zero()));
+
+ return transition;
+}
+
+- (void)setFillExtrusionOpacity:(MGLStyleValue<NSNumber *> *)fillExtrusionOpacity {
+ MGLAssertStyleLayerIsValid();
+
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(fillExtrusionOpacity);
+ self.rawLayer->setFillExtrusionOpacity(mbglValue);
+}
+
+- (MGLStyleValue<NSNumber *> *)fillExtrusionOpacity {
+ MGLAssertStyleLayerIsValid();
+
+ auto propertyValue = self.rawLayer->getFillExtrusionOpacity();
+ if (propertyValue.isUndefined()) {
+ return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultFillExtrusionOpacity());
+ }
+ return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
+}
+
+- (void)setFillExtrusionOpacityTransition:(MGLTransition )transition {
+ MGLAssertStyleLayerIsValid();
+
+ mbgl::style::TransitionOptions options { { MGLDurationFromTimeInterval(transition.duration) }, { MGLDurationFromTimeInterval(transition.delay) } };
+ self.rawLayer->setFillExtrusionOpacityTransition(options);
+}
+
+- (MGLTransition)fillExtrusionOpacityTransition {
+ MGLAssertStyleLayerIsValid();
+
+ mbgl::style::TransitionOptions transitionOptions = self.rawLayer->getFillExtrusionOpacityTransition();
+ MGLTransition transition;
+ transition.duration = MGLTimeIntervalFromDuration(transitionOptions.duration.value_or(mbgl::Duration::zero()));
+ transition.delay = MGLTimeIntervalFromDuration(transitionOptions.delay.value_or(mbgl::Duration::zero()));
+
+ return transition;
+}
+
+- (void)setFillExtrusionPattern:(MGLStyleValue<NSString *> *)fillExtrusionPattern {
+ MGLAssertStyleLayerIsValid();
+
+ auto mbglValue = MGLStyleValueTransformer<std::string, NSString *>().toPropertyValue(fillExtrusionPattern);
+ self.rawLayer->setFillExtrusionPattern(mbglValue);
+}
+
+- (MGLStyleValue<NSString *> *)fillExtrusionPattern {
+ MGLAssertStyleLayerIsValid();
+
+ auto propertyValue = self.rawLayer->getFillExtrusionPattern();
+ if (propertyValue.isUndefined()) {
+ return MGLStyleValueTransformer<std::string, NSString *>().toStyleValue(self.rawLayer->getDefaultFillExtrusionPattern());
+ }
+ return MGLStyleValueTransformer<std::string, NSString *>().toStyleValue(propertyValue);
+}
+
+- (void)setFillExtrusionPatternTransition:(MGLTransition )transition {
+ MGLAssertStyleLayerIsValid();
+
+ mbgl::style::TransitionOptions options { { MGLDurationFromTimeInterval(transition.duration) }, { MGLDurationFromTimeInterval(transition.delay) } };
+ self.rawLayer->setFillExtrusionPatternTransition(options);
+}
+
+- (MGLTransition)fillExtrusionPatternTransition {
+ MGLAssertStyleLayerIsValid();
+
+ mbgl::style::TransitionOptions transitionOptions = self.rawLayer->getFillExtrusionPatternTransition();
+ MGLTransition transition;
+ transition.duration = MGLTimeIntervalFromDuration(transitionOptions.duration.value_or(mbgl::Duration::zero()));
+ transition.delay = MGLTimeIntervalFromDuration(transitionOptions.delay.value_or(mbgl::Duration::zero()));
+
+ return transition;
+}
+
+- (void)setFillExtrusionTranslation:(MGLStyleValue<NSValue *> *)fillExtrusionTranslation {
+ MGLAssertStyleLayerIsValid();
+
+ auto mbglValue = MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toInterpolatablePropertyValue(fillExtrusionTranslation);
+ self.rawLayer->setFillExtrusionTranslate(mbglValue);
+}
+
+- (MGLStyleValue<NSValue *> *)fillExtrusionTranslation {
+ MGLAssertStyleLayerIsValid();
+
+ auto propertyValue = self.rawLayer->getFillExtrusionTranslate();
+ if (propertyValue.isUndefined()) {
+ return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toStyleValue(self.rawLayer->getDefaultFillExtrusionTranslate());
+ }
+ return MGLStyleValueTransformer<std::array<float, 2>, NSValue *>().toStyleValue(propertyValue);
+}
+
+- (void)setFillExtrusionTranslationTransition:(MGLTransition )transition {
+ MGLAssertStyleLayerIsValid();
+
+ mbgl::style::TransitionOptions options { { MGLDurationFromTimeInterval(transition.duration) }, { MGLDurationFromTimeInterval(transition.delay) } };
+ self.rawLayer->setFillExtrusionTranslateTransition(options);
+}
+
+- (MGLTransition)fillExtrusionTranslationTransition {
+ MGLAssertStyleLayerIsValid();
+
+ mbgl::style::TransitionOptions transitionOptions = self.rawLayer->getFillExtrusionTranslateTransition();
+ MGLTransition transition;
+ transition.duration = MGLTimeIntervalFromDuration(transitionOptions.duration.value_or(mbgl::Duration::zero()));
+ transition.delay = MGLTimeIntervalFromDuration(transitionOptions.delay.value_or(mbgl::Duration::zero()));
+
+ return transition;
+}
+
+- (void)setFillExtrusionTranslate:(MGLStyleValue<NSValue *> *)fillExtrusionTranslate {
+}
+
+- (MGLStyleValue<NSValue *> *)fillExtrusionTranslate {
+ return self.fillExtrusionTranslation;
+}
+
+- (void)setFillExtrusionTranslationAnchor:(MGLStyleValue<NSValue *> *)fillExtrusionTranslationAnchor {
+ MGLAssertStyleLayerIsValid();
+
+ auto mbglValue = MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLFillExtrusionTranslationAnchor>().toEnumPropertyValue(fillExtrusionTranslationAnchor);
+ self.rawLayer->setFillExtrusionTranslateAnchor(mbglValue);
+}
+
+- (MGLStyleValue<NSValue *> *)fillExtrusionTranslationAnchor {
+ MGLAssertStyleLayerIsValid();
+
+ auto propertyValue = self.rawLayer->getFillExtrusionTranslateAnchor();
+ if (propertyValue.isUndefined()) {
+ return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLFillExtrusionTranslationAnchor>().toEnumStyleValue(self.rawLayer->getDefaultFillExtrusionTranslateAnchor());
+ }
+ return MGLStyleValueTransformer<mbgl::style::TranslateAnchorType, NSValue *, mbgl::style::TranslateAnchorType, MGLFillExtrusionTranslationAnchor>().toEnumStyleValue(propertyValue);
+}
+
+- (void)setFillExtrusionTranslateAnchor:(MGLStyleValue<NSValue *> *)fillExtrusionTranslateAnchor {
+}
+
+- (MGLStyleValue<NSValue *> *)fillExtrusionTranslateAnchor {
+ return self.fillExtrusionTranslationAnchor;
+}
+
+@end
+
+@implementation NSValue (MGLFillExtrusionStyleLayerAdditions)
+
++ (NSValue *)valueWithMGLFillExtrusionTranslationAnchor:(MGLFillExtrusionTranslationAnchor)fillExtrusionTranslationAnchor {
+ return [NSValue value:&fillExtrusionTranslationAnchor withObjCType:@encode(MGLFillExtrusionTranslationAnchor)];
+}
+
+- (MGLFillExtrusionTranslationAnchor)MGLFillExtrusionTranslationAnchorValue {
+ MGLFillExtrusionTranslationAnchor fillExtrusionTranslationAnchor;
+ [self getValue:&fillExtrusionTranslationAnchor];
+ return fillExtrusionTranslationAnchor;
+}
+
+@end
diff --git a/platform/darwin/src/MGLStyle.mm b/platform/darwin/src/MGLStyle.mm
index a4cf8d9cce..81b6446e7f 100644
--- a/platform/darwin/src/MGLStyle.mm
+++ b/platform/darwin/src/MGLStyle.mm
@@ -3,6 +3,7 @@
#import "MGLMapView_Private.h"
#import "MGLStyleLayer.h"
#import "MGLFillStyleLayer.h"
+#import "MGLFillExtrusionStyleLayer.h"
#import "MGLLineStyleLayer.h"
#import "MGLCircleStyleLayer.h"
#import "MGLSymbolStyleLayer.h"
@@ -28,6 +29,7 @@
#include <mbgl/util/default_styles.hpp>
#include <mbgl/style/image.hpp>
#include <mbgl/style/layers/fill_layer.hpp>
+#include <mbgl/style/layers/fill_extrusion_layer.hpp>
#include <mbgl/style/layers/line_layer.hpp>
#include <mbgl/style/layers/symbol_layer.hpp>
#include <mbgl/style/layers/raster_layer.hpp>
@@ -332,6 +334,8 @@ static NSURL *MGLStyleURL_emerald;
if (auto fillLayer = rawLayer->as<mbgl::style::FillLayer>()) {
return [[MGLFillStyleLayer alloc] initWithRawLayer:fillLayer];
+ } else if (auto fillExtrusionLayer = rawLayer->as<mbgl::style::FillExtrusionLayer>()) {
+ return [[MGLFillExtrusionStyleLayer alloc] initWithRawLayer:fillExtrusionLayer];
} else if (auto lineLayer = rawLayer->as<mbgl::style::LineLayer>()) {
return [[MGLLineStyleLayer alloc] initWithRawLayer:lineLayer];
} else if (auto symbolLayer = rawLayer->as<mbgl::style::SymbolLayer>()) {
diff --git a/platform/darwin/src/MGLStyleLayer.h.ejs b/platform/darwin/src/MGLStyleLayer.h.ejs
index e29ea4611d..df42621c6d 100644
--- a/platform/darwin/src/MGLStyleLayer.h.ejs
+++ b/platform/darwin/src/MGLStyleLayer.h.ejs
@@ -161,7 +161,7 @@ which it is added.
*/
@interface NSValue (MGL<%- camelize(type) %>StyleLayerAdditions)
-#pragma mark Working with <%- camelize(type) %> Style Layer Attribute Values
+#pragma mark Working with <%- camelize(unhyphenate(type)) %> Style Layer Attribute Values
<% for (let property of enumProperties) { -%>
/**
diff --git a/platform/darwin/test/MGLDocumentationExampleTests.swift b/platform/darwin/test/MGLDocumentationExampleTests.swift
index 177d97d523..6d2dc597a9 100644
--- a/platform/darwin/test/MGLDocumentationExampleTests.swift
+++ b/platform/darwin/test/MGLDocumentationExampleTests.swift
@@ -158,6 +158,22 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
XCTAssertNotNil(mapView.style?.layer(withIdentifier: "parks"))
}
+
+ func testMGLFillExtrusionStyleLayer() {
+ let buildings = MGLVectorSource(identifier: "buildings", configurationURL: URL(string: "https://example.com/style.json")!)
+ mapView.style?.addSource(buildings)
+
+ //#-example-code
+ let layer = MGLFillExtrusionStyleLayer(identifier: "buildings", source: buildings)
+ layer.sourceLayerIdentifier = "building"
+ layer.fillExtrusionHeight = MGLStyleValue(interpolationMode: .identity, sourceStops: nil, attributeName: "height", options: nil)
+ layer.fillExtrusionBase = MGLStyleValue(interpolationMode: .identity, sourceStops: nil, attributeName: "min_height", options: nil)
+ layer.predicate = NSPredicate(format: "extrude == TRUE")
+ mapView.style?.addLayer(layer)
+ //#-end-example-code
+
+ XCTAssertNotNil(mapView.style?.layer(withIdentifier: "buildings"))
+ }
func testMGLSymbolStyleLayer() {
let pois = MGLVectorSource(identifier: "pois", configurationURL: URL(string: "https://example.com/style.json")!)
diff --git a/platform/darwin/test/MGLFillExtrusionStyleLayerTests.mm b/platform/darwin/test/MGLFillExtrusionStyleLayerTests.mm
new file mode 100644
index 0000000000..5d99c815ea
--- /dev/null
+++ b/platform/darwin/test/MGLFillExtrusionStyleLayerTests.mm
@@ -0,0 +1,445 @@
+// This file is generated.
+// Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
+
+#import "MGLStyleLayerTests.h"
+#import "../../darwin/src/NSDate+MGLAdditions.h"
+
+#import "MGLStyleLayer_Private.h"
+
+#include <mbgl/style/layers/fill_extrusion_layer.hpp>
+#include <mbgl/style/transition_options.hpp>
+
+@interface MGLFillExtrusionLayerTests : MGLStyleLayerTests
+@end
+
+@implementation MGLFillExtrusionLayerTests
+
++ (NSString *)layerType {
+ return @"fill-extrusion";
+}
+
+- (void)testPredicates {
+ MGLPointFeature *feature = [[MGLPointFeature alloc] init];
+ MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"sourceID" shape:feature options:nil];
+ MGLFillExtrusionStyleLayer *layer = [[MGLFillExtrusionStyleLayer alloc] initWithIdentifier:@"layerID" source:source];
+
+ XCTAssertNil(layer.sourceLayerIdentifier);
+ layer.sourceLayerIdentifier = @"layerID";
+ XCTAssertEqualObjects(layer.sourceLayerIdentifier, @"layerID");
+ layer.sourceLayerIdentifier = nil;
+ XCTAssertNil(layer.sourceLayerIdentifier);
+
+ XCTAssertNil(layer.predicate);
+ layer.predicate = [NSPredicate predicateWithValue:NO];
+ XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithValue:NO]);
+ layer.predicate = nil;
+ XCTAssertNil(layer.predicate);
+}
+
+- (void)testProperties {
+ MGLPointFeature *feature = [[MGLPointFeature alloc] init];
+ MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"sourceID" shape:feature options:nil];
+
+ MGLFillExtrusionStyleLayer *layer = [[MGLFillExtrusionStyleLayer alloc] initWithIdentifier:@"layerID" source:source];
+ XCTAssertNotEqual(layer.rawLayer, nullptr);
+ XCTAssertTrue(layer.rawLayer->is<mbgl::style::FillExtrusionLayer>());
+ auto rawLayer = layer.rawLayer->as<mbgl::style::FillExtrusionLayer>();
+
+ MGLTransition transitionTest = MGLTransitionMake(5, 4);
+
+
+ // fill-extrusion-base
+ {
+ XCTAssertTrue(rawLayer->getFillExtrusionBase().isUndefined(),
+ @"fill-extrusion-base should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.fillExtrusionBase;
+
+ MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.fillExtrusionBase = constantStyleValue;
+ mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getFillExtrusionBase(), propertyValue,
+ @"Setting fillExtrusionBase to a constant value should update fill-extrusion-base.");
+ XCTAssertEqualObjects(layer.fillExtrusionBase, constantStyleValue,
+ @"fillExtrusionBase should round-trip constant values.");
+
+ MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
+ layer.fillExtrusionBase = functionStyleValue;
+
+ mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
+
+ XCTAssertEqual(rawLayer->getFillExtrusionBase(), propertyValue,
+ @"Setting fillExtrusionBase to a camera function should update fill-extrusion-base.");
+ XCTAssertEqualObjects(layer.fillExtrusionBase, functionStyleValue,
+ @"fillExtrusionBase should round-trip camera functions.");
+
+ functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
+ layer.fillExtrusionBase = functionStyleValue;
+
+ mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
+ propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops };
+
+ XCTAssertEqual(rawLayer->getFillExtrusionBase(), propertyValue,
+ @"Setting fillExtrusionBase to a source function should update fill-extrusion-base.");
+ XCTAssertEqualObjects(layer.fillExtrusionBase, functionStyleValue,
+ @"fillExtrusionBase should round-trip source functions.");
+
+ functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
+ layer.fillExtrusionBase = functionStyleValue;
+
+ std::map<float, float> innerStops { {18, 0xff} };
+ mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 };
+
+ propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops };
+
+ XCTAssertEqual(rawLayer->getFillExtrusionBase(), propertyValue,
+ @"Setting fillExtrusionBase to a composite function should update fill-extrusion-base.");
+ XCTAssertEqualObjects(layer.fillExtrusionBase, functionStyleValue,
+ @"fillExtrusionBase should round-trip composite functions.");
+
+
+ layer.fillExtrusionBase = nil;
+ XCTAssertTrue(rawLayer->getFillExtrusionBase().isUndefined(),
+ @"Unsetting fillExtrusionBase should return fill-extrusion-base to the default value.");
+ XCTAssertEqualObjects(layer.fillExtrusionBase, defaultStyleValue,
+ @"fillExtrusionBase should return the default value after being unset.");
+ // Transition property test
+ layer.fillExtrusionBaseTransition = transitionTest;
+ auto toptions = rawLayer->getFillExtrusionBaseTransition();
+ XCTAssert(toptions.delay && MGLTimeIntervalFromDuration(*toptions.delay) == transitionTest.delay);
+ XCTAssert(toptions.duration && MGLTimeIntervalFromDuration(*toptions.duration) == transitionTest.duration);
+
+ MGLTransition fillExtrusionBaseTransition = layer.fillExtrusionBaseTransition;
+ XCTAssertEqual(fillExtrusionBaseTransition.delay, transitionTest.delay);
+ XCTAssertEqual(fillExtrusionBaseTransition.duration, transitionTest.duration);
+ }
+
+ // fill-extrusion-color
+ {
+ XCTAssertTrue(rawLayer->getFillExtrusionColor().isUndefined(),
+ @"fill-extrusion-color should be unset initially.");
+ MGLStyleValue<MGLColor *> *defaultStyleValue = layer.fillExtrusionColor;
+
+ MGLStyleValue<MGLColor *> *constantStyleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]];
+ layer.fillExtrusionColor = constantStyleValue;
+ mbgl::style::DataDrivenPropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } };
+ XCTAssertEqual(rawLayer->getFillExtrusionColor(), propertyValue,
+ @"Setting fillExtrusionColor to a constant value should update fill-extrusion-color.");
+ XCTAssertEqualObjects(layer.fillExtrusionColor, constantStyleValue,
+ @"fillExtrusionColor should round-trip constant values.");
+
+ MGLStyleValue<MGLColor *> * functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
+ layer.fillExtrusionColor = functionStyleValue;
+
+ mbgl::style::IntervalStops<mbgl::Color> intervalStops = { {{18, { 1, 0, 0, 1 }}} };
+ propertyValue = mbgl::style::CameraFunction<mbgl::Color> { intervalStops };
+
+ XCTAssertEqual(rawLayer->getFillExtrusionColor(), propertyValue,
+ @"Setting fillExtrusionColor to a camera function should update fill-extrusion-color.");
+ XCTAssertEqualObjects(layer.fillExtrusionColor, functionStyleValue,
+ @"fillExtrusionColor should round-trip camera functions.");
+
+ functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
+ layer.fillExtrusionColor = functionStyleValue;
+
+ mbgl::style::ExponentialStops<mbgl::Color> exponentialStops = { {{18, { 1, 0, 0, 1 }}}, 1.0 };
+ propertyValue = mbgl::style::SourceFunction<mbgl::Color> { "keyName", exponentialStops };
+
+ XCTAssertEqual(rawLayer->getFillExtrusionColor(), propertyValue,
+ @"Setting fillExtrusionColor to a source function should update fill-extrusion-color.");
+ XCTAssertEqualObjects(layer.fillExtrusionColor, functionStyleValue,
+ @"fillExtrusionColor should round-trip source functions.");
+
+ functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
+ layer.fillExtrusionColor = functionStyleValue;
+
+ std::map<float, mbgl::Color> innerStops { {18, { 1, 0, 0, 1 }} };
+ mbgl::style::CompositeExponentialStops<mbgl::Color> compositeStops { { {10.0, innerStops} }, 1.0 };
+
+ propertyValue = mbgl::style::CompositeFunction<mbgl::Color> { "keyName", compositeStops };
+
+ XCTAssertEqual(rawLayer->getFillExtrusionColor(), propertyValue,
+ @"Setting fillExtrusionColor to a composite function should update fill-extrusion-color.");
+ XCTAssertEqualObjects(layer.fillExtrusionColor, functionStyleValue,
+ @"fillExtrusionColor should round-trip composite functions.");
+
+
+ layer.fillExtrusionColor = nil;
+ XCTAssertTrue(rawLayer->getFillExtrusionColor().isUndefined(),
+ @"Unsetting fillExtrusionColor should return fill-extrusion-color to the default value.");
+ XCTAssertEqualObjects(layer.fillExtrusionColor, defaultStyleValue,
+ @"fillExtrusionColor should return the default value after being unset.");
+ // Transition property test
+ layer.fillExtrusionColorTransition = transitionTest;
+ auto toptions = rawLayer->getFillExtrusionColorTransition();
+ XCTAssert(toptions.delay && MGLTimeIntervalFromDuration(*toptions.delay) == transitionTest.delay);
+ XCTAssert(toptions.duration && MGLTimeIntervalFromDuration(*toptions.duration) == transitionTest.duration);
+
+ MGLTransition fillExtrusionColorTransition = layer.fillExtrusionColorTransition;
+ XCTAssertEqual(fillExtrusionColorTransition.delay, transitionTest.delay);
+ XCTAssertEqual(fillExtrusionColorTransition.duration, transitionTest.duration);
+ }
+
+ // fill-extrusion-height
+ {
+ XCTAssertTrue(rawLayer->getFillExtrusionHeight().isUndefined(),
+ @"fill-extrusion-height should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.fillExtrusionHeight;
+
+ MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.fillExtrusionHeight = constantStyleValue;
+ mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getFillExtrusionHeight(), propertyValue,
+ @"Setting fillExtrusionHeight to a constant value should update fill-extrusion-height.");
+ XCTAssertEqualObjects(layer.fillExtrusionHeight, constantStyleValue,
+ @"fillExtrusionHeight should round-trip constant values.");
+
+ MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
+ layer.fillExtrusionHeight = functionStyleValue;
+
+ mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
+
+ XCTAssertEqual(rawLayer->getFillExtrusionHeight(), propertyValue,
+ @"Setting fillExtrusionHeight to a camera function should update fill-extrusion-height.");
+ XCTAssertEqualObjects(layer.fillExtrusionHeight, functionStyleValue,
+ @"fillExtrusionHeight should round-trip camera functions.");
+
+ functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
+ layer.fillExtrusionHeight = functionStyleValue;
+
+ mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
+ propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops };
+
+ XCTAssertEqual(rawLayer->getFillExtrusionHeight(), propertyValue,
+ @"Setting fillExtrusionHeight to a source function should update fill-extrusion-height.");
+ XCTAssertEqualObjects(layer.fillExtrusionHeight, functionStyleValue,
+ @"fillExtrusionHeight should round-trip source functions.");
+
+ functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
+ layer.fillExtrusionHeight = functionStyleValue;
+
+ std::map<float, float> innerStops { {18, 0xff} };
+ mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 };
+
+ propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops };
+
+ XCTAssertEqual(rawLayer->getFillExtrusionHeight(), propertyValue,
+ @"Setting fillExtrusionHeight to a composite function should update fill-extrusion-height.");
+ XCTAssertEqualObjects(layer.fillExtrusionHeight, functionStyleValue,
+ @"fillExtrusionHeight should round-trip composite functions.");
+
+
+ layer.fillExtrusionHeight = nil;
+ XCTAssertTrue(rawLayer->getFillExtrusionHeight().isUndefined(),
+ @"Unsetting fillExtrusionHeight should return fill-extrusion-height to the default value.");
+ XCTAssertEqualObjects(layer.fillExtrusionHeight, defaultStyleValue,
+ @"fillExtrusionHeight should return the default value after being unset.");
+ // Transition property test
+ layer.fillExtrusionHeightTransition = transitionTest;
+ auto toptions = rawLayer->getFillExtrusionHeightTransition();
+ XCTAssert(toptions.delay && MGLTimeIntervalFromDuration(*toptions.delay) == transitionTest.delay);
+ XCTAssert(toptions.duration && MGLTimeIntervalFromDuration(*toptions.duration) == transitionTest.duration);
+
+ MGLTransition fillExtrusionHeightTransition = layer.fillExtrusionHeightTransition;
+ XCTAssertEqual(fillExtrusionHeightTransition.delay, transitionTest.delay);
+ XCTAssertEqual(fillExtrusionHeightTransition.duration, transitionTest.duration);
+ }
+
+ // fill-extrusion-opacity
+ {
+ XCTAssertTrue(rawLayer->getFillExtrusionOpacity().isUndefined(),
+ @"fill-extrusion-opacity should be unset initially.");
+ MGLStyleValue<NSNumber *> *defaultStyleValue = layer.fillExtrusionOpacity;
+
+ MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
+ layer.fillExtrusionOpacity = constantStyleValue;
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ XCTAssertEqual(rawLayer->getFillExtrusionOpacity(), propertyValue,
+ @"Setting fillExtrusionOpacity to a constant value should update fill-extrusion-opacity.");
+ XCTAssertEqualObjects(layer.fillExtrusionOpacity, constantStyleValue,
+ @"fillExtrusionOpacity should round-trip constant values.");
+
+ MGLStyleValue<NSNumber *> * functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
+ layer.fillExtrusionOpacity = functionStyleValue;
+
+ mbgl::style::IntervalStops<float> intervalStops = { {{18, 0xff}} };
+ propertyValue = mbgl::style::CameraFunction<float> { intervalStops };
+
+ XCTAssertEqual(rawLayer->getFillExtrusionOpacity(), propertyValue,
+ @"Setting fillExtrusionOpacity to a camera function should update fill-extrusion-opacity.");
+ XCTAssertEqualObjects(layer.fillExtrusionOpacity, functionStyleValue,
+ @"fillExtrusionOpacity should round-trip camera functions.");
+
+
+
+ layer.fillExtrusionOpacity = nil;
+ XCTAssertTrue(rawLayer->getFillExtrusionOpacity().isUndefined(),
+ @"Unsetting fillExtrusionOpacity should return fill-extrusion-opacity to the default value.");
+ XCTAssertEqualObjects(layer.fillExtrusionOpacity, defaultStyleValue,
+ @"fillExtrusionOpacity should return the default value after being unset.");
+
+ functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
+ XCTAssertThrowsSpecificNamed(layer.fillExtrusionOpacity = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
+ XCTAssertThrowsSpecificNamed(layer.fillExtrusionOpacity = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ // Transition property test
+ layer.fillExtrusionOpacityTransition = transitionTest;
+ auto toptions = rawLayer->getFillExtrusionOpacityTransition();
+ XCTAssert(toptions.delay && MGLTimeIntervalFromDuration(*toptions.delay) == transitionTest.delay);
+ XCTAssert(toptions.duration && MGLTimeIntervalFromDuration(*toptions.duration) == transitionTest.duration);
+
+ MGLTransition fillExtrusionOpacityTransition = layer.fillExtrusionOpacityTransition;
+ XCTAssertEqual(fillExtrusionOpacityTransition.delay, transitionTest.delay);
+ XCTAssertEqual(fillExtrusionOpacityTransition.duration, transitionTest.duration);
+ }
+
+ // fill-extrusion-pattern
+ {
+ XCTAssertTrue(rawLayer->getFillExtrusionPattern().isUndefined(),
+ @"fill-extrusion-pattern should be unset initially.");
+ MGLStyleValue<NSString *> *defaultStyleValue = layer.fillExtrusionPattern;
+
+ MGLStyleValue<NSString *> *constantStyleValue = [MGLStyleValue<NSString *> valueWithRawValue:@"Fill Extrusion Pattern"];
+ layer.fillExtrusionPattern = constantStyleValue;
+ mbgl::style::PropertyValue<std::string> propertyValue = { "Fill Extrusion Pattern" };
+ XCTAssertEqual(rawLayer->getFillExtrusionPattern(), propertyValue,
+ @"Setting fillExtrusionPattern to a constant value should update fill-extrusion-pattern.");
+ XCTAssertEqualObjects(layer.fillExtrusionPattern, constantStyleValue,
+ @"fillExtrusionPattern should round-trip constant values.");
+
+ MGLStyleValue<NSString *> * functionStyleValue = [MGLStyleValue<NSString *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
+ layer.fillExtrusionPattern = functionStyleValue;
+
+ mbgl::style::IntervalStops<std::string> intervalStops = { {{18, "Fill Extrusion Pattern"}} };
+ propertyValue = mbgl::style::CameraFunction<std::string> { intervalStops };
+
+ XCTAssertEqual(rawLayer->getFillExtrusionPattern(), propertyValue,
+ @"Setting fillExtrusionPattern to a camera function should update fill-extrusion-pattern.");
+ XCTAssertEqualObjects(layer.fillExtrusionPattern, functionStyleValue,
+ @"fillExtrusionPattern should round-trip camera functions.");
+
+
+
+ layer.fillExtrusionPattern = nil;
+ XCTAssertTrue(rawLayer->getFillExtrusionPattern().isUndefined(),
+ @"Unsetting fillExtrusionPattern should return fill-extrusion-pattern to the default value.");
+ XCTAssertEqualObjects(layer.fillExtrusionPattern, defaultStyleValue,
+ @"fillExtrusionPattern should return the default value after being unset.");
+
+ functionStyleValue = [MGLStyleValue<NSString *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
+ XCTAssertThrowsSpecificNamed(layer.fillExtrusionPattern = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionStyleValue = [MGLStyleValue<NSString *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
+ XCTAssertThrowsSpecificNamed(layer.fillExtrusionPattern = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ // Transition property test
+ layer.fillExtrusionPatternTransition = transitionTest;
+ auto toptions = rawLayer->getFillExtrusionPatternTransition();
+ XCTAssert(toptions.delay && MGLTimeIntervalFromDuration(*toptions.delay) == transitionTest.delay);
+ XCTAssert(toptions.duration && MGLTimeIntervalFromDuration(*toptions.duration) == transitionTest.duration);
+
+ MGLTransition fillExtrusionPatternTransition = layer.fillExtrusionPatternTransition;
+ XCTAssertEqual(fillExtrusionPatternTransition.delay, transitionTest.delay);
+ XCTAssertEqual(fillExtrusionPatternTransition.duration, transitionTest.duration);
+ }
+
+ // fill-extrusion-translate
+ {
+ XCTAssertTrue(rawLayer->getFillExtrusionTranslate().isUndefined(),
+ @"fill-extrusion-translate should be unset initially.");
+ MGLStyleValue<NSValue *> *defaultStyleValue = layer.fillExtrusionTranslation;
+
+ MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:
+#if TARGET_OS_IPHONE
+ [NSValue valueWithCGVector:CGVectorMake(1, 1)]
+#else
+ [NSValue valueWithMGLVector:CGVectorMake(1, -1)]
+#endif
+ ];
+ layer.fillExtrusionTranslation = constantStyleValue;
+ mbgl::style::PropertyValue<std::array<float, 2>> propertyValue = { { 1, 1 } };
+ XCTAssertEqual(rawLayer->getFillExtrusionTranslate(), propertyValue,
+ @"Setting fillExtrusionTranslation to a constant value should update fill-extrusion-translate.");
+ XCTAssertEqualObjects(layer.fillExtrusionTranslation, constantStyleValue,
+ @"fillExtrusionTranslation should round-trip constant values.");
+
+ MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
+ layer.fillExtrusionTranslation = functionStyleValue;
+
+ mbgl::style::IntervalStops<std::array<float, 2>> intervalStops = { {{18, { 1, 1 }}} };
+ propertyValue = mbgl::style::CameraFunction<std::array<float, 2>> { intervalStops };
+
+ XCTAssertEqual(rawLayer->getFillExtrusionTranslate(), propertyValue,
+ @"Setting fillExtrusionTranslation to a camera function should update fill-extrusion-translate.");
+ XCTAssertEqualObjects(layer.fillExtrusionTranslation, functionStyleValue,
+ @"fillExtrusionTranslation should round-trip camera functions.");
+
+
+
+ layer.fillExtrusionTranslation = nil;
+ XCTAssertTrue(rawLayer->getFillExtrusionTranslate().isUndefined(),
+ @"Unsetting fillExtrusionTranslation should return fill-extrusion-translate to the default value.");
+ XCTAssertEqualObjects(layer.fillExtrusionTranslation, defaultStyleValue,
+ @"fillExtrusionTranslation should return the default value after being unset.");
+
+ functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
+ XCTAssertThrowsSpecificNamed(layer.fillExtrusionTranslation = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
+ XCTAssertThrowsSpecificNamed(layer.fillExtrusionTranslation = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ }
+
+ // fill-extrusion-translate-anchor
+ {
+ XCTAssertTrue(rawLayer->getFillExtrusionTranslateAnchor().isUndefined(),
+ @"fill-extrusion-translate-anchor should be unset initially.");
+ MGLStyleValue<NSValue *> *defaultStyleValue = layer.fillExtrusionTranslationAnchor;
+
+ MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLFillExtrusionTranslationAnchor:MGLFillExtrusionTranslationAnchorViewport]];
+ layer.fillExtrusionTranslationAnchor = constantStyleValue;
+ mbgl::style::PropertyValue<mbgl::style::TranslateAnchorType> propertyValue = { mbgl::style::TranslateAnchorType::Viewport };
+ XCTAssertEqual(rawLayer->getFillExtrusionTranslateAnchor(), propertyValue,
+ @"Setting fillExtrusionTranslationAnchor to a constant value should update fill-extrusion-translate-anchor.");
+ XCTAssertEqualObjects(layer.fillExtrusionTranslationAnchor, constantStyleValue,
+ @"fillExtrusionTranslationAnchor should round-trip constant values.");
+
+ MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
+ layer.fillExtrusionTranslationAnchor = functionStyleValue;
+
+ mbgl::style::IntervalStops<mbgl::style::TranslateAnchorType> intervalStops = { {{18, mbgl::style::TranslateAnchorType::Viewport}} };
+ propertyValue = mbgl::style::CameraFunction<mbgl::style::TranslateAnchorType> { intervalStops };
+
+ XCTAssertEqual(rawLayer->getFillExtrusionTranslateAnchor(), propertyValue,
+ @"Setting fillExtrusionTranslationAnchor to a camera function should update fill-extrusion-translate-anchor.");
+ XCTAssertEqualObjects(layer.fillExtrusionTranslationAnchor, functionStyleValue,
+ @"fillExtrusionTranslationAnchor should round-trip camera functions.");
+
+
+
+ layer.fillExtrusionTranslationAnchor = nil;
+ XCTAssertTrue(rawLayer->getFillExtrusionTranslateAnchor().isUndefined(),
+ @"Unsetting fillExtrusionTranslationAnchor should return fill-extrusion-translate-anchor to the default value.");
+ XCTAssertEqualObjects(layer.fillExtrusionTranslationAnchor, defaultStyleValue,
+ @"fillExtrusionTranslationAnchor should return the default value after being unset.");
+
+ functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
+ XCTAssertThrowsSpecificNamed(layer.fillExtrusionTranslationAnchor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
+ XCTAssertThrowsSpecificNamed(layer.fillExtrusionTranslationAnchor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
+ }
+}
+
+- (void)testPropertyNames {
+ [self testPropertyName:@"fill-extrusion-base" isBoolean:NO];
+ [self testPropertyName:@"fill-extrusion-color" isBoolean:NO];
+ [self testPropertyName:@"fill-extrusion-height" isBoolean:NO];
+ [self testPropertyName:@"fill-extrusion-opacity" isBoolean:NO];
+ [self testPropertyName:@"fill-extrusion-pattern" isBoolean:NO];
+ [self testPropertyName:@"fill-extrusion-translation" isBoolean:NO];
+ [self testPropertyName:@"fill-extrusion-translation-anchor" isBoolean:NO];
+}
+
+- (void)testValueAdditions {
+ XCTAssertEqual([NSValue valueWithMGLFillExtrusionTranslationAnchor:MGLFillExtrusionTranslationAnchorMap].MGLFillExtrusionTranslationAnchorValue, MGLFillExtrusionTranslationAnchorMap);
+ XCTAssertEqual([NSValue valueWithMGLFillExtrusionTranslationAnchor:MGLFillExtrusionTranslationAnchorViewport].MGLFillExtrusionTranslationAnchorValue, MGLFillExtrusionTranslationAnchorViewport);
+}
+
+@end
diff --git a/platform/darwin/test/MGLStyleLayerTests.mm.ejs b/platform/darwin/test/MGLStyleLayerTests.mm.ejs
index a405ae58c4..5fdfc3d44e 100644
--- a/platform/darwin/test/MGLStyleLayerTests.mm.ejs
+++ b/platform/darwin/test/MGLStyleLayerTests.mm.ejs
@@ -11,7 +11,7 @@
#import "MGLStyleLayer_Private.h"
-#include <mbgl/style/layers/<%- type %>_layer.hpp>
+#include <mbgl/style/layers/<%- type.replace('-', '_') %>_layer.hpp>
#include <mbgl/style/transition_options.hpp>
@interface MGL<%- camelize(type) %>LayerTests : MGLStyleLayerTests
diff --git a/platform/glfw/glfw_view.cpp b/platform/glfw/glfw_view.cpp
index f4ade26885..26c946ed76 100644
--- a/platform/glfw/glfw_view.cpp
+++ b/platform/glfw/glfw_view.cpp
@@ -22,7 +22,7 @@ GLFWView::GLFWView(bool fullscreen_, bool benchmark_)
: fullscreen(fullscreen_), benchmark(benchmark_) {
glfwSetErrorCallback(glfwError);
- std::srand(std::time(nullptr));
+ std::srand(static_cast<unsigned int>(std::time(nullptr)));
if (!glfwInit()) {
mbgl::Log::Error(mbgl::Event::OpenGL, "failed to initialize glfw");
@@ -542,7 +542,7 @@ void showDebugImage(std::string name, const char *data, size_t width, size_t hei
static GLFWwindow *debugWindow = nullptr;
if (!debugWindow) {
- debugWindow = glfwCreateWindow(width, height, name.c_str(), nullptr, nullptr);
+ debugWindow = glfwCreateWindow(static_cast<int>(width), static_cast<int>(height), name.c_str(), nullptr, nullptr);
if (!debugWindow) {
glfwTerminate();
fprintf(stderr, "Failed to initialize window\n");
@@ -552,7 +552,7 @@ void showDebugImage(std::string name, const char *data, size_t width, size_t hei
GLFWwindow *currentWindow = glfwGetCurrentContext();
- glfwSetWindowSize(debugWindow, width, height);
+ glfwSetWindowSize(debugWindow, static_cast<int>(width), static_cast<int>(height));
glfwMakeContextCurrent(debugWindow);
int fbWidth, fbHeight;
@@ -573,7 +573,7 @@ void showColorDebugImage(std::string name, const char *data, size_t logicalWidth
static GLFWwindow *debugWindow = nullptr;
if (!debugWindow) {
- debugWindow = glfwCreateWindow(logicalWidth, logicalHeight, name.c_str(), nullptr, nullptr);
+ debugWindow = glfwCreateWindow(static_cast<int>(logicalWidth), static_cast<int>(logicalHeight), name.c_str(), nullptr, nullptr);
if (!debugWindow) {
glfwTerminate();
fprintf(stderr, "Failed to initialize window\n");
@@ -583,7 +583,7 @@ void showColorDebugImage(std::string name, const char *data, size_t logicalWidth
GLFWwindow *currentWindow = glfwGetCurrentContext();
- glfwSetWindowSize(debugWindow, logicalWidth, logicalHeight);
+ glfwSetWindowSize(debugWindow, static_cast<int>(logicalWidth), static_cast<int>(logicalHeight));
glfwMakeContextCurrent(debugWindow);
int fbWidth, fbHeight;
diff --git a/platform/glfw/main.cpp b/platform/glfw/main.cpp
index 172b7e3284..59d2ce3ec6 100644
--- a/platform/glfw/main.cpp
+++ b/platform/glfw/main.cpp
@@ -199,4 +199,4 @@ int main(int argc, char *argv[]) {
view = nullptr;
return 0;
-}
+} \ No newline at end of file
diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md
index d72076274c..fb35070a79 100644
--- a/platform/ios/CHANGELOG.md
+++ b/platform/ios/CHANGELOG.md
@@ -17,6 +17,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT
* Xcode 8.0 or higher is now recommended for using this SDK. ([#8775](https://github.com/mapbox/mapbox-gl-native/pull/8775))
* Updated MGLMapView’s logo view to display [the new Mapbox logo](https://www.mapbox.com/blog/new-mapbox-logo/). ([#8771](https://github.com/mapbox/mapbox-gl-native/pull/8771))
* Fixed a crash or console spew when MGLMapView is initialized with a frame smaller than 64 points wide by 64 points tall. ([#8562](https://github.com/mapbox/mapbox-gl-native/pull/8562))
+* Added support for 3D extrusion of buildings and other polygonal features via the `MGLFillExtrusionStyleLayer` class and the `fill-extrusion` layer type in style JSON. ([#8431](https://github.com/mapbox/mapbox-gl-native/pull/8431))
* The error passed into `-[MGLMapViewDelegate mapViewDidFailLoadingMap:withError:]` now includes a more specific description and failure reason. ([#8418](https://github.com/mapbox/mapbox-gl-native/pull/8418))
* Fixed an issue where gesture recognizers associated with map view interactivity were not disabled when their related interactions were disabled. ([#8304](https://github.com/mapbox/mapbox-gl-native/pull/8304))
* Fixed an issue where re-adding a layer that had been previously removed from a style would reset its paint properties. Moved initializers for `MGLTileSource`, `MGLStyleLayer`, and `MGLForegroundStyleLayer` to their concrete subclasses; because these classes were already intended for initialization only via concrete subclasses, this should have no developer impact. ([#8626](https://github.com/mapbox/mapbox-gl-native/pull/8626))
diff --git a/platform/ios/docs/guides/For Style Authors.md b/platform/ios/docs/guides/For Style Authors.md
index ed9018b121..fee4d70b34 100644
--- a/platform/ios/docs/guides/For Style Authors.md
+++ b/platform/ios/docs/guides/For Style Authors.md
@@ -181,6 +181,7 @@ In style JSON | In the SDK
`background` | `MGLBackgroundStyleLayer`
`circle` | `MGLCircleStyleLayer`
`fill` | `MGLFillStyleLayer`
+`fill-extrusion` | `MGLFillExtrusionStyleLayer`
`line` | `MGLLineStyleLayer`
`raster` | `MGLRasterStyleLayer`
`symbol` | `MGLSymbolStyleLayer`
@@ -206,6 +207,13 @@ In style JSON | In Objective-C | In Swift
`fill-translate` | `MGLFillStyleLayer.fillTranslation` | `MGLFillStyleLayer.fillTranslation`
`fill-translate-anchor` | `MGLFillStyleLayer.fillTranslationAnchor` | `MGLFillStyleLayer.fillTranslationAnchor`
+### Fill extrusion style layers
+
+In style JSON | In Objective-C | In Swift
+--------------|----------------|---------
+`fill-extrusion-translate` | `MGLFillExtrusionStyleLayer.fillExtrusionTranslation` | `MGLFillExtrusionStyleLayer.fillExtrusionTranslation`
+`fill-extrusion-translate-anchor` | `MGLFillExtrusionStyleLayer.fillExtrusionTranslationAnchor` | `MGLFillExtrusionStyleLayer.fillExtrusionTranslationAnchor`
+
### Line style layers
In style JSON | In Objective-C | In Swift
diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj
index 1d25a9679e..a071022844 100644
--- a/platform/ios/ios.xcodeproj/project.pbxproj
+++ b/platform/ios/ios.xcodeproj/project.pbxproj
@@ -453,6 +453,11 @@
DD4823761D94AE6C00EB71B7 /* line_filter_style.json in Resources */ = {isa = PBXBuildFile; fileRef = DD4823731D94AE6C00EB71B7 /* line_filter_style.json */; };
DD4823771D94AE6C00EB71B7 /* numeric_filter_style.json in Resources */ = {isa = PBXBuildFile; fileRef = DD4823741D94AE6C00EB71B7 /* numeric_filter_style.json */; };
DD58A4C61D822BD000E1F038 /* MGLExpressionTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DD58A4C51D822BD000E1F038 /* MGLExpressionTests.mm */; };
+ FA68F14A1E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = FA68F1481E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ FA68F14B1E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = FA68F1481E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ FA68F14D1E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = FA68F1491E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.mm */; };
+ FA68F14E1E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = FA68F1491E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.mm */; };
+ FAE1CDCB1E9D79CB00C40B5B /* MGLFillExtrusionStyleLayerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = FAE1CDC81E9D79C600C40B5B /* MGLFillExtrusionStyleLayerTests.mm */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -914,6 +919,9 @@
DD4823731D94AE6C00EB71B7 /* line_filter_style.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = line_filter_style.json; sourceTree = "<group>"; };
DD4823741D94AE6C00EB71B7 /* numeric_filter_style.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = numeric_filter_style.json; sourceTree = "<group>"; };
DD58A4C51D822BD000E1F038 /* MGLExpressionTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLExpressionTests.mm; path = ../../darwin/test/MGLExpressionTests.mm; sourceTree = "<group>"; };
+ FA68F1481E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLFillExtrusionStyleLayer.h; sourceTree = "<group>"; };
+ FA68F1491E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLFillExtrusionStyleLayer.mm; sourceTree = "<group>"; };
+ FAE1CDC81E9D79C600C40B5B /* MGLFillExtrusionStyleLayerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLFillExtrusionStyleLayerTests.mm; path = ../../darwin/test/MGLFillExtrusionStyleLayerTests.mm; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -990,6 +998,8 @@
35136D381D42271A00C20EFD /* MGLBackgroundStyleLayer.mm */,
353933F11D3FB753003F57D7 /* MGLCircleStyleLayer.h */,
35136D3B1D42272500C20EFD /* MGLCircleStyleLayer.mm */,
+ FA68F1481E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.h */,
+ FA68F1491E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.mm */,
35D13AC11D3D19DD00AFB4E0 /* MGLFillStyleLayer.h */,
35D13AC21D3D19DD00AFB4E0 /* MGLFillStyleLayer.mm */,
3538AA1B1D542239008EC33D /* MGLForegroundStyleLayer.h */,
@@ -1061,6 +1071,7 @@
DA2DBBCC1D51E80400D38FF9 /* MGLStyleLayerTests.h */,
DA2DBBCD1D51E80400D38FF9 /* MGLStyleLayerTests.m */,
DA3C6FF21E2859E700F962BE /* test-Bridging-Header.h */,
+ FAE1CDC81E9D79C600C40B5B /* MGLFillExtrusionStyleLayerTests.mm */,
3575797F1D501E09000B822E /* MGLFillStyleLayerTests.mm */,
357579821D502AE6000B822E /* MGLRasterStyleLayerTests.mm */,
357579841D502AF5000B822E /* MGLSymbolStyleLayerTests.mm */,
@@ -1636,6 +1647,7 @@
40CF6DBB1DAC3C6600A4D18B /* MGLShape_Private.h in Headers */,
4018B1CA1CDC288E00F666AF /* MGLAnnotationView.h in Headers */,
35E79F201D41266300957B9E /* MGLStyleLayer_Private.h in Headers */,
+ FA68F14A1E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.h in Headers */,
353933FB1D3FB7C0003F57D7 /* MGLRasterStyleLayer.h in Headers */,
DA8847EF1CBAFA5100AB86E3 /* MGLAccountManager.h in Headers */,
DA8848511CBAFB9800AB86E3 /* MGLAPIClient.h in Headers */,
@@ -1719,6 +1731,7 @@
35B82BF91D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h in Headers */,
DA35A2CA1CCAAAD200E826B2 /* NSValue+MGLAdditions.h in Headers */,
350098BC1D480108004B2AF0 /* MGLVectorSource.h in Headers */,
+ FA68F14B1E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.h in Headers */,
353933FC1D3FB7C0003F57D7 /* MGLRasterStyleLayer.h in Headers */,
3566C76D1D4A8DFA008152BC /* MGLRasterSource.h in Headers */,
DAED38641D62D0FC00D7640F /* NSURL+MGLAdditions.h in Headers */,
@@ -2151,6 +2164,7 @@
DA2E88621CC0382C00F24E7B /* MGLOfflinePackTests.m in Sources */,
55E2AD131E5B125400E8C587 /* MGLOfflineStorageTests.mm in Sources */,
920A3E5D1E6F995200C16EFC /* MGLSourceQueryTests.m in Sources */,
+ FAE1CDCB1E9D79CB00C40B5B /* MGLFillExtrusionStyleLayerTests.mm in Sources */,
DA35A2AA1CCA058D00E826B2 /* MGLCoordinateFormatterTests.m in Sources */,
357579831D502AE6000B822E /* MGLRasterStyleLayerTests.mm in Sources */,
353D23961D0B0DFE002BE09D /* MGLAnnotationViewTests.m in Sources */,
@@ -2216,6 +2230,7 @@
DA88481F1CBAFA6200AB86E3 /* MGLMultiPoint.mm in Sources */,
DA88482B1CBAFA6200AB86E3 /* MGLTypes.m in Sources */,
4018B1C71CDC287F00F666AF /* MGLAnnotationView.mm in Sources */,
+ FA68F14D1E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.mm in Sources */,
404C26E41D89B877000AA13D /* MGLTileSource.mm in Sources */,
355AE0011E9281DA00F3939D /* MGLScaleBar.mm in Sources */,
DA88481D1CBAFA6200AB86E3 /* MGLMapCamera.mm in Sources */,
@@ -2295,6 +2310,7 @@
DAA4E4301CBB730400178DFB /* MGLLocationManager.m in Sources */,
DAA4E4321CBB730400178DFB /* MGLMapView.mm in Sources */,
DAA4E41E1CBB730400178DFB /* MGLMapCamera.mm in Sources */,
+ FA68F14E1E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.mm in Sources */,
404C26E51D89B877000AA13D /* MGLTileSource.mm in Sources */,
355AE0021E9281DA00F3939D /* MGLScaleBar.mm in Sources */,
4018B1C81CDC287F00F666AF /* MGLAnnotationView.mm in Sources */,
diff --git a/platform/ios/jazzy.yml b/platform/ios/jazzy.yml
index 87af09a9b9..e0ce29beba 100644
--- a/platform/ios/jazzy.yml
+++ b/platform/ios/jazzy.yml
@@ -85,6 +85,7 @@ custom_categories:
- MGLVectorStyleLayer
- MGLCircleStyleLayer
- MGLFillStyleLayer
+ - MGLFillExtrusionStyleLayer
- MGLLineStyleLayer
- MGLSymbolStyleLayer
- name: Offline Maps
diff --git a/platform/ios/src/Mapbox.h b/platform/ios/src/Mapbox.h
index 2623777d8f..9a9dc702ca 100644
--- a/platform/ios/src/Mapbox.h
+++ b/platform/ios/src/Mapbox.h
@@ -38,6 +38,7 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[];
#import "MGLStyleLayer.h"
#import "MGLForegroundStyleLayer.h"
#import "MGLVectorStyleLayer.h"
+#import "MGLFillExtrusionStyleLayer.h"
#import "MGLFillStyleLayer.h"
#import "MGLLineStyleLayer.h"
#import "MGLSymbolStyleLayer.h"
diff --git a/platform/ios/vendor/SMCalloutView b/platform/ios/vendor/SMCalloutView
-Subproject d6ecaba377c9f963aef630faf86e3b8f8cdb88d
+Subproject 2aede5d8d1577101bf18405246220e7a710df60
diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md
index 0e3f9b8fbf..73fa638692 100644
--- a/platform/macos/CHANGELOG.md
+++ b/platform/macos/CHANGELOG.md
@@ -14,6 +14,7 @@
* Updated MGLMapView’s logo view to display [the new Mapbox logo](https://www.mapbox.com/blog/new-mapbox-logo/). ([#8771](https://github.com/mapbox/mapbox-gl-native/pull/8771))
* Fixed an issue causing attribution button text to appear blue instead of black. ([#8701](https://github.com/mapbox/mapbox-gl-native/pull/8701))
* Fixed a crash or console spew when MGLMapView is initialized with a frame smaller than 64 points wide by 64 points tall. ([#8562](https://github.com/mapbox/mapbox-gl-native/pull/8562))
+* Added support for 3D extrusion of buildings and other polygonal features via the `MGLFillExtrusionStyleLayer` class and the `fill-extrusion` layer type in style JSON. ([#8431](https://github.com/mapbox/mapbox-gl-native/pull/8431))
* The error passed into `-[MGLMapViewDelegate mapViewDidFailLoadingMap:withError:]` now includes a more specific description and failure reason. ([#8418](https://github.com/mapbox/mapbox-gl-native/pull/8418))
* Fixed an issue where re-adding a layer that had been previously removed from a style would reset its paint properties. Moved initializers for `MGLTileSource`, `MGLStyleLayer`, and `MGLForegroundStyleLayer` to their concrete subclasses; because these classes were already intended for initialization only via concrete subclasses, this should have no developer impact. ([#8626](https://github.com/mapbox/mapbox-gl-native/pull/8626))
diff --git a/platform/macos/docs/guides/For Style Authors.md b/platform/macos/docs/guides/For Style Authors.md
index 9ed69ac9ea..b9163582b4 100644
--- a/platform/macos/docs/guides/For Style Authors.md
+++ b/platform/macos/docs/guides/For Style Authors.md
@@ -170,6 +170,7 @@ In style JSON | In the SDK
`background` | `MGLBackgroundStyleLayer`
`circle` | `MGLCircleStyleLayer`
`fill` | `MGLFillStyleLayer`
+`fill-extrusion` | `MGLFillExtrusionStyleLayer`
`line` | `MGLLineStyleLayer`
`raster` | `MGLRasterStyleLayer`
`symbol` | `MGLSymbolStyleLayer`
@@ -195,6 +196,13 @@ In style JSON | In Objective-C | In Swift
`fill-translate` | `MGLFillStyleLayer.fillTranslation` | `MGLFillStyleLayer.fillTranslation`
`fill-translate-anchor` | `MGLFillStyleLayer.fillTranslationAnchor` | `MGLFillStyleLayer.fillTranslationAnchor`
+### Fill extrusion style layers
+
+In style JSON | In Objective-C | In Swift
+--------------|----------------|---------
+`fill-extrusion-translate` | `MGLFillExtrusionStyleLayer.fillExtrusionTranslation` | `MGLFillExtrusionStyleLayer.fillExtrusionTranslation`
+`fill-extrusion-translate-anchor` | `MGLFillExtrusionStyleLayer.fillExtrusionTranslationAnchor` | `MGLFillExtrusionStyleLayer.fillExtrusionTranslationAnchor`
+
### Line style layers
In style JSON | In Objective-C | In Swift
diff --git a/platform/macos/jazzy.yml b/platform/macos/jazzy.yml
index 1f0e2fbc74..3c24b351cc 100644
--- a/platform/macos/jazzy.yml
+++ b/platform/macos/jazzy.yml
@@ -71,6 +71,7 @@ custom_categories:
- MGLVectorStyleLayer
- MGLCircleStyleLayer
- MGLFillStyleLayer
+ - MGLFillExtrusionStyleLayer
- MGLLineStyleLayer
- MGLSymbolStyleLayer
- name: Offline Maps
diff --git a/platform/macos/macos.xcodeproj/project.pbxproj b/platform/macos/macos.xcodeproj/project.pbxproj
index 175629d126..97adf445e1 100644
--- a/platform/macos/macos.xcodeproj/project.pbxproj
+++ b/platform/macos/macos.xcodeproj/project.pbxproj
@@ -139,6 +139,9 @@
DA8F25B21D51CB270010E6B5 /* NSValue+MGLStyleAttributeAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8F25A61D51CB270010E6B5 /* NSValue+MGLStyleAttributeAdditions.h */; };
DA8F25B31D51CB270010E6B5 /* NSValue+MGLStyleAttributeAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA8F25A71D51CB270010E6B5 /* NSValue+MGLStyleAttributeAdditions.mm */; };
DAA48EFD1D6A4731006A7E36 /* StyleLayerIconTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = DAA48EFC1D6A4731006A7E36 /* StyleLayerIconTransformer.m */; };
+ DAA998FB1E9F545C002E6EA6 /* MGLFillExtrusionStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = DAA998F91E9F545C002E6EA6 /* MGLFillExtrusionStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DAA998FC1E9F545C002E6EA6 /* MGLFillExtrusionStyleLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = DAA998FA1E9F545C002E6EA6 /* MGLFillExtrusionStyleLayer.mm */; };
+ DAA999011E9F5EC5002E6EA6 /* MGLFillExtrusionStyleLayerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DAA999001E9F5EC5002E6EA6 /* MGLFillExtrusionStyleLayerTests.mm */; };
DAB2CCE51DF632ED001B2FE1 /* LimeGreenStyleLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = DAB2CCE41DF632ED001B2FE1 /* LimeGreenStyleLayer.m */; };
DAC2ABC51CC6D343006D18C4 /* MGLAnnotationImage_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DAC2ABC41CC6D343006D18C4 /* MGLAnnotationImage_Private.h */; };
DACB0C391E18DFFD005DDBEA /* MGLStyle+MBXAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DACB0C381E18DFFD005DDBEA /* MGLStyle+MBXAdditions.m */; };
@@ -453,6 +456,9 @@
DAA32CC11E4C4F93006F8D24 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Localizable.strings; sourceTree = "<group>"; };
DAA48EFB1D6A4731006A7E36 /* StyleLayerIconTransformer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StyleLayerIconTransformer.h; sourceTree = "<group>"; };
DAA48EFC1D6A4731006A7E36 /* StyleLayerIconTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StyleLayerIconTransformer.m; sourceTree = "<group>"; };
+ DAA998F91E9F545C002E6EA6 /* MGLFillExtrusionStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLFillExtrusionStyleLayer.h; sourceTree = "<group>"; };
+ DAA998FA1E9F545C002E6EA6 /* MGLFillExtrusionStyleLayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLFillExtrusionStyleLayer.mm; sourceTree = "<group>"; };
+ DAA999001E9F5EC5002E6EA6 /* MGLFillExtrusionStyleLayerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLFillExtrusionStyleLayerTests.mm; sourceTree = "<group>"; };
DAB2CCE31DF632ED001B2FE1 /* LimeGreenStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LimeGreenStyleLayer.h; sourceTree = "<group>"; };
DAB2CCE41DF632ED001B2FE1 /* LimeGreenStyleLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LimeGreenStyleLayer.m; sourceTree = "<group>"; };
DAC2ABC41CC6D343006D18C4 /* MGLAnnotationImage_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAnnotationImage_Private.h; sourceTree = "<group>"; };
@@ -599,6 +605,8 @@
DA8F25861D51C9E10010E6B5 /* MGLBackgroundStyleLayer.mm */,
3527428B1D4C24AB00A1ECE6 /* MGLCircleStyleLayer.h */,
3527428C1D4C24AB00A1ECE6 /* MGLCircleStyleLayer.mm */,
+ DAA998F91E9F545C002E6EA6 /* MGLFillExtrusionStyleLayer.h */,
+ DAA998FA1E9F545C002E6EA6 /* MGLFillExtrusionStyleLayer.mm */,
35602BF81D3EA99F0050646F /* MGLFillStyleLayer.h */,
35602BF91D3EA99F0050646F /* MGLFillStyleLayer.mm */,
35602BFD1D3EA9B40050646F /* MGLForegroundStyleLayer.h */,
@@ -785,6 +793,7 @@
40E1601A1DF216E6005EA6D9 /* MGLStyleLayerTests.h */,
40E1601B1DF216E6005EA6D9 /* MGLStyleLayerTests.m */,
DA2207BA1DC076930002F84D /* test-Bridging-Header.h */,
+ DAA999001E9F5EC5002E6EA6 /* MGLFillExtrusionStyleLayerTests.mm */,
DA8F25741D51C5F40010E6B5 /* MGLFillStyleLayerTests.mm */,
DA8F25751D51C5F40010E6B5 /* MGLRasterStyleLayerTests.mm */,
DA8F25761D51C5F40010E6B5 /* MGLSymbolStyleLayerTests.mm */,
@@ -1078,6 +1087,7 @@
3527428D1D4C24AB00A1ECE6 /* MGLCircleStyleLayer.h in Headers */,
DA00FC8A1D5EEAC3009AABC8 /* MGLAttributionInfo.h in Headers */,
DAE6C3B21CC31EF300DB3429 /* MGLAttributionButton.h in Headers */,
+ DAA998FB1E9F545C002E6EA6 /* MGLFillExtrusionStyleLayer.h in Headers */,
40B77E451DB11BC9003DA2FE /* NSArray+MGLAdditions.h in Headers */,
35C5D8471D6DD66D00E95907 /* NSComparisonPredicate+MGLAdditions.h in Headers */,
DAE6C3A31CC31E9400DB3429 /* MGLAnnotationImage.h in Headers */,
@@ -1405,6 +1415,7 @@
DAE6C3911CC31E2A00DB3429 /* MGLPolygon.mm in Sources */,
35C6DF851E214C0400ACA483 /* MGLDistanceFormatter.m in Sources */,
DAE6C39B1CC31E2A00DB3429 /* NSProcessInfo+MGLAdditions.m in Sources */,
+ DAA998FC1E9F545C002E6EA6 /* MGLFillExtrusionStyleLayer.mm in Sources */,
DAE6C38F1CC31E2A00DB3429 /* MGLOfflineStorage.mm in Sources */,
DAED38601D62CED700D7640F /* NSURL+MGLAdditions.m in Sources */,
35C5D84A1D6DD66D00E95907 /* NSCompoundPredicate+MGLAdditions.mm in Sources */,
@@ -1449,6 +1460,7 @@
4031ACFC1E9EB3C100A3EA26 /* MGLMapViewDelegateIntegrationTests.swift in Sources */,
4031AD031E9FD6AA00A3EA26 /* MGLSDKTestHelpers.swift in Sources */,
DA87A9A71DCACC5000810D09 /* MGLBackgroundStyleLayerTests.mm in Sources */,
+ DAA999011E9F5EC5002E6EA6 /* MGLFillExtrusionStyleLayerTests.mm in Sources */,
DA29875A1E1A4290002299F5 /* MGLDocumentationExampleTests.swift in Sources */,
DAE6C3D31CC34C9900DB3429 /* MGLOfflinePackTests.m in Sources */,
DA87A9A51DCACC5000810D09 /* MGLLineStyleLayerTests.mm in Sources */,
diff --git a/platform/macos/src/Mapbox.h b/platform/macos/src/Mapbox.h
index 79ecfb21dc..dcb5b50b8f 100644
--- a/platform/macos/src/Mapbox.h
+++ b/platform/macos/src/Mapbox.h
@@ -37,6 +37,7 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[];
#import "MGLForegroundStyleLayer.h"
#import "MGLVectorStyleLayer.h"
#import "MGLFillStyleLayer.h"
+#import "MGLFillExtrusionStyleLayer.h"
#import "MGLLineStyleLayer.h"
#import "MGLSymbolStyleLayer.h"
#import "MGLRasterStyleLayer.h"
diff --git a/scripts/generate-shaders.js b/scripts/generate-shaders.js
index cffe9d3854..2f1f4c2f9c 100755
--- a/scripts/generate-shaders.js
+++ b/scripts/generate-shaders.js
@@ -47,7 +47,10 @@ ${fragmentPrelude}
'circle',
'collision_box',
'debug',
+ 'extrusion_texture',
'fill',
+ 'fill_extrusion',
+ 'fill_extrusion_pattern',
'fill_outline',
'fill_outline_pattern',
'fill_pattern',
diff --git a/scripts/style-code.js b/scripts/style-code.js
index 156934a240..70914c5fb6 100644
--- a/scripts/style-code.js
+++ b/scripts/style-code.js
@@ -22,6 +22,10 @@ global.snakeCaseUpper = function snakeCaseUpper(str) {
return str.replace(/-/g, "_").toUpperCase();
};
+global.unhyphenate = function (str) {
+ return str.replace(/-/g, " ");
+};
+
global.writeIfModified = function(filename, newContent) {
try {
const oldContent = fs.readFileSync(filename, 'utf8');
diff --git a/src/mbgl/gl/context.cpp b/src/mbgl/gl/context.cpp
index 565fe33771..1a5a6a2289 100644
--- a/src/mbgl/gl/context.cpp
+++ b/src/mbgl/gl/context.cpp
@@ -250,6 +250,7 @@ UniqueRenderbuffer Context::createRenderbuffer(const RenderbufferType type, cons
bindRenderbuffer = renderbuffer;
MBGL_CHECK_ERROR(
glRenderbufferStorage(GL_RENDERBUFFER, static_cast<GLenum>(type), size.width, size.height));
+ bindRenderbuffer = 0;
return renderbuffer;
}
@@ -384,6 +385,17 @@ Framebuffer Context::createFramebuffer(const Texture& color) {
return { color.size, std::move(fbo) };
}
+Framebuffer
+Context::createFramebuffer(const Texture& color,
+ const Renderbuffer<RenderbufferType::DepthComponent>& depthTarget) {
+ auto fbo = createFramebuffer();
+ bindFramebuffer = fbo;
+ MBGL_CHECK_ERROR(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color.texture, 0));
+ MBGL_CHECK_ERROR(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthTarget.renderbuffer));
+ checkFramebuffer();
+ return { depthTarget.size, std::move(fbo) };
+}
+
UniqueTexture
Context::createTexture(const Size size, const void* data, TextureFormat format, TextureUnit unit) {
auto obj = createTexture();
diff --git a/src/mbgl/gl/context.hpp b/src/mbgl/gl/context.hpp
index 14af299baa..56c0618989 100644
--- a/src/mbgl/gl/context.hpp
+++ b/src/mbgl/gl/context.hpp
@@ -81,7 +81,9 @@ public:
template <RenderbufferType type>
Renderbuffer<type> createRenderbuffer(const Size size) {
- static_assert(type == RenderbufferType::RGBA || type == RenderbufferType::DepthStencil,
+ static_assert(type == RenderbufferType::RGBA ||
+ type == RenderbufferType::DepthStencil ||
+ type == RenderbufferType::DepthComponent,
"invalid renderbuffer type");
return { size, createRenderbuffer(type, size) };
}
@@ -92,6 +94,8 @@ public:
Framebuffer createFramebuffer(const Texture&,
const Renderbuffer<RenderbufferType::DepthStencil>&);
Framebuffer createFramebuffer(const Texture&);
+ Framebuffer createFramebuffer(const Texture&,
+ const Renderbuffer<RenderbufferType::DepthComponent>&);
template <typename Image,
TextureFormat format = Image::channels == 4 ? TextureFormat::RGBA
diff --git a/src/mbgl/gl/types.hpp b/src/mbgl/gl/types.hpp
index 7d436693c9..0595419674 100644
--- a/src/mbgl/gl/types.hpp
+++ b/src/mbgl/gl/types.hpp
@@ -37,6 +37,11 @@ enum class DataType : uint32_t {
enum class RenderbufferType : uint32_t {
RGBA = 0x8058,
DepthStencil = 0x88F0,
+#if not MBGL_USE_GLES2
+ DepthComponent = 0x1902, // GL_DEPTH_COMPONENT
+#else
+ DepthComponent = 0x81A5, // GL_DEPTH_COMPONENT16
+#endif // MBGL_USE_GLES2
};
enum class TextureMipMap : bool { No = false, Yes = true };
diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp
index dd33d0b170..b5a1af172a 100644
--- a/src/mbgl/map/map.cpp
+++ b/src/mbgl/map/map.cpp
@@ -9,6 +9,7 @@
#include <mbgl/style/style.hpp>
#include <mbgl/style/source.hpp>
#include <mbgl/style/layer.hpp>
+#include <mbgl/style/light.hpp>
#include <mbgl/style/observer.hpp>
#include <mbgl/style/transition_options.hpp>
#include <mbgl/style/update_parameters.hpp>
@@ -978,6 +979,22 @@ const style::Image* Map::getImage(const std::string& id) {
return nullptr;
}
+void Map::setLight(std::unique_ptr<style::Light> light) {
+ if (!impl->style) {
+ return;
+ }
+
+ impl->style->light = std::move(light);
+}
+
+style::Light* Map::getLight() {
+ if (!impl->style) {
+ return nullptr;
+ }
+
+ return impl->style->light.get();
+}
+
#pragma mark - Defaults
std::string Map::getStyleName() const {
diff --git a/src/mbgl/map/transform_state.cpp b/src/mbgl/map/transform_state.cpp
index fe90d4b2e4..8c3c70feec 100644
--- a/src/mbgl/map/transform_state.cpp
+++ b/src/mbgl/map/transform_state.cpp
@@ -2,6 +2,7 @@
#include <mbgl/tile/tile_id.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/interpolate.hpp>
+#include <mbgl/util/projection.hpp>
#include <mbgl/math/log2.hpp>
#include <mbgl/math/clamp.hpp>
@@ -26,7 +27,7 @@ void TransformState::matrixFor(mat4& matrix, const UnwrappedTileID& tileID) cons
matrix::scale(matrix, matrix, s / util::EXTENT, s / util::EXTENT, 1);
}
-void TransformState::getProjMatrix(mat4& projMatrix) const {
+void TransformState::getProjMatrix(mat4& projMatrix, uint16_t nearZ) const {
if (size.isEmpty()) {
return;
}
@@ -45,7 +46,7 @@ void TransformState::getProjMatrix(mat4& projMatrix) const {
// Add a bit extra to avoid precision problems when a fragment's distance is exactly `furthestDistance`
const double farZ = furthestDistance * 1.01;
- matrix::perspective(projMatrix, getFieldOfView(), double(size.width) / size.height, 1, farZ);
+ matrix::perspective(projMatrix, getFieldOfView(), double(size.width) / size.height, nearZ, farZ);
const bool flippedY = viewportMode == ViewportMode::FlippedY;
matrix::scale(projMatrix, projMatrix, 1, flippedY ? 1 : -1, 1);
@@ -64,6 +65,9 @@ void TransformState::getProjMatrix(mat4& projMatrix) const {
matrix::translate(projMatrix, projMatrix, pixel_x() - size.width / 2.0f,
pixel_y() - size.height / 2.0f, 0);
+
+ matrix::scale(projMatrix, projMatrix, 1, 1,
+ 1.0 / Projection::getMetersPerPixelAtLatitude(getLatLng(LatLng::Unwrapped).latitude(), getZoom()));
}
#pragma mark - Dimensions
@@ -233,7 +237,6 @@ bool TransformState::isGestureInProgress() const {
return gestureInProgress;
}
-
#pragma mark - Projection
double TransformState::zoomScale(double zoom) const {
diff --git a/src/mbgl/map/transform_state.hpp b/src/mbgl/map/transform_state.hpp
index 85d5d96700..d0bf455ead 100644
--- a/src/mbgl/map/transform_state.hpp
+++ b/src/mbgl/map/transform_state.hpp
@@ -24,7 +24,7 @@ public:
// Matrix
void matrixFor(mat4&, const UnwrappedTileID&) const;
- void getProjMatrix(mat4& matrix) const;
+ void getProjMatrix(mat4& matrix, uint16_t nearZ = 1) const;
// Dimensions
Size getSize() const;
diff --git a/src/mbgl/programs/attributes.hpp b/src/mbgl/programs/attributes.hpp
index e9ca18927e..cfd6a629de 100644
--- a/src/mbgl/programs/attributes.hpp
+++ b/src/mbgl/programs/attributes.hpp
@@ -24,6 +24,8 @@ MBGL_DEFINE_ATTRIBUTE(int16_t, 2, a_pos);
MBGL_DEFINE_ATTRIBUTE(int16_t, 2, a_extrude);
MBGL_DEFINE_ATTRIBUTE(int16_t, 4, a_pos_offset);
MBGL_DEFINE_ATTRIBUTE(uint16_t, 2, a_texture_pos);
+MBGL_DEFINE_ATTRIBUTE(int16_t, 3, a_normal);
+MBGL_DEFINE_ATTRIBUTE(uint16_t, 1, a_edgedistance);
template <typename T, std::size_t N>
struct a_data {
diff --git a/src/mbgl/programs/extrusion_texture_program.cpp b/src/mbgl/programs/extrusion_texture_program.cpp
new file mode 100644
index 0000000000..afda4384b7
--- /dev/null
+++ b/src/mbgl/programs/extrusion_texture_program.cpp
@@ -0,0 +1,7 @@
+#include <mbgl/programs/extrusion_texture_program.hpp>
+
+namespace mbgl {
+
+static_assert(sizeof(ExtrusionTextureLayoutVertex) == 4, "expected ExtrusionTextureLayoutVertex size");
+
+} // namespace mbgl
diff --git a/src/mbgl/programs/extrusion_texture_program.hpp b/src/mbgl/programs/extrusion_texture_program.hpp
new file mode 100644
index 0000000000..1519aa095d
--- /dev/null
+++ b/src/mbgl/programs/extrusion_texture_program.hpp
@@ -0,0 +1,37 @@
+#pragma once
+
+#include <mbgl/programs/program.hpp>
+#include <mbgl/programs/attributes.hpp>
+#include <mbgl/programs/uniforms.hpp>
+#include <mbgl/shaders/extrusion_texture.hpp>
+#include <mbgl/util/geometry.hpp>
+
+namespace mbgl {
+
+class ExtrusionTextureProgram : public Program<
+ shaders::extrusion_texture,
+ gl::Triangle,
+ gl::Attributes<attributes::a_pos>,
+ gl::Uniforms<
+ uniforms::u_matrix,
+ uniforms::u_world,
+ uniforms::u_image,
+ uniforms::u_opacity>,
+ style::PaintProperties<>> {
+public:
+ using Program::Program;
+
+ static LayoutVertex layoutVertex(Point<int16_t> p) {
+ return LayoutVertex{
+ {{
+ p.x,
+ p.y
+ }}
+ };
+ }
+};
+
+using ExtrusionTextureLayoutVertex = ExtrusionTextureProgram::LayoutVertex;
+using ExtrusionTextureAttributes = ExtrusionTextureProgram::Attributes;
+
+} // namespace mbgl
diff --git a/src/mbgl/programs/fill_extrusion_program.cpp b/src/mbgl/programs/fill_extrusion_program.cpp
new file mode 100644
index 0000000000..67426c8d9d
--- /dev/null
+++ b/src/mbgl/programs/fill_extrusion_program.cpp
@@ -0,0 +1,81 @@
+#include <mbgl/programs/fill_extrusion_program.hpp>
+#include <mbgl/sprite/sprite_atlas.hpp>
+#include <mbgl/style/cross_faded_property_evaluator.hpp>
+#include <mbgl/tile/tile_id.hpp>
+#include <mbgl/map/transform_state.hpp>
+#include <mbgl/util/mat3.hpp>
+
+namespace mbgl {
+
+using namespace style;
+
+static_assert(sizeof(FillExtrusionLayoutVertex) == 12, "expected FillExtrusionLayoutVertex size");
+
+std::array<float, 3> lightColor(const EvaluatedLight& light) {
+ const auto color = light.get<LightColor>();
+ return {{ color.r, color.g, color.b }};
+}
+
+std::array<float, 3> lightPosition(const EvaluatedLight& light, const TransformState& state) {
+ auto lightPos = light.get<LightPosition>().getCartesian();
+ mat3 lightMat;
+ matrix::identity(lightMat);
+ if (light.get<LightAnchor>() == LightAnchorType::Viewport) {
+ matrix::rotate(lightMat, lightMat, -state.getAngle());
+ }
+ matrix::transformMat3f(lightPos, lightPos, lightMat);
+ return lightPos;
+}
+
+float lightIntensity(const EvaluatedLight& light) {
+ return light.get<LightIntensity>();
+}
+
+FillExtrusionUniforms::Values
+FillExtrusionUniforms::values(mat4 matrix,
+ const TransformState& state,
+ const EvaluatedLight& light) {
+ return FillExtrusionUniforms::Values{
+ uniforms::u_matrix::Value{ matrix },
+ uniforms::u_lightcolor::Value{ lightColor(light) },
+ uniforms::u_lightpos::Value{ lightPosition(light, state) },
+ uniforms::u_lightintensity::Value{ lightIntensity(light) }
+ };
+}
+
+FillExtrusionPatternUniforms::Values
+FillExtrusionPatternUniforms::values(mat4 matrix,
+ const SpriteAtlasElement& a,
+ const SpriteAtlasElement& b,
+ const Faded<std::string>& fading,
+ const UnwrappedTileID& tileID,
+ const TransformState& state,
+ const float heightFactor,
+ const EvaluatedLight& light) {
+ int32_t tileSizeAtNearestZoom = util::tileSize * state.zoomScale(state.getIntegerZoom() - tileID.canonical.z);
+ int32_t pixelX = tileSizeAtNearestZoom * (tileID.canonical.x + tileID.wrap * state.zoomScale(tileID.canonical.z));
+ int32_t pixelY = tileSizeAtNearestZoom * tileID.canonical.y;
+
+ return FillExtrusionPatternUniforms::Values{
+ uniforms::u_matrix::Value{ matrix },
+ uniforms::u_pattern_tl_a::Value{ a.tl },
+ uniforms::u_pattern_br_a::Value{ a.br },
+ uniforms::u_pattern_tl_b::Value{ b.tl },
+ uniforms::u_pattern_br_b::Value{ b.br },
+ uniforms::u_pattern_size_a::Value{ a.size },
+ uniforms::u_pattern_size_b::Value{ b.size },
+ uniforms::u_scale_a::Value{ fading.fromScale },
+ uniforms::u_scale_b::Value{ fading.toScale },
+ uniforms::u_mix::Value{ fading.t },
+ uniforms::u_image::Value{ 0 },
+ uniforms::u_pixel_coord_upper::Value{ std::array<float, 2>{{ float(pixelX >> 16), float(pixelY >> 16) }} },
+ uniforms::u_pixel_coord_lower::Value{ std::array<float, 2>{{ float(pixelX & 0xFFFF), float(pixelY & 0xFFFF) }} },
+ uniforms::u_tile_units_to_pixels::Value{ 1.0f / tileID.pixelsToTileUnits(1.0f, state.getIntegerZoom()) },
+ uniforms::u_height_factor::Value{ heightFactor },
+ uniforms::u_lightcolor::Value{ lightColor(light) },
+ uniforms::u_lightpos::Value{ lightPosition(light, state) },
+ uniforms::u_lightintensity::Value{ lightIntensity(light) },
+ };
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/programs/fill_extrusion_program.hpp b/src/mbgl/programs/fill_extrusion_program.hpp
new file mode 100644
index 0000000000..b84e50298c
--- /dev/null
+++ b/src/mbgl/programs/fill_extrusion_program.hpp
@@ -0,0 +1,129 @@
+#pragma once
+
+#include <mbgl/programs/program.hpp>
+#include <mbgl/programs/attributes.hpp>
+#include <mbgl/programs/uniforms.hpp>
+#include <mbgl/shaders/fill_extrusion.hpp>
+#include <mbgl/shaders/fill_extrusion_pattern.hpp>
+#include <mbgl/util/geometry.hpp>
+#include <mbgl/util/mat4.hpp>
+#include <mbgl/util/size.hpp>
+#include <mbgl/style/layers/fill_extrusion_layer_properties.hpp>
+#include <mbgl/style/style.hpp>
+#include <mbgl/style/light_impl.hpp>
+
+#include <string>
+
+namespace mbgl {
+
+class SpriteAtlasElement;
+class UnwrappedTileID;
+class TransformState;
+
+namespace style {
+template <class> class Faded;
+} // namespace style
+
+namespace uniforms {
+MBGL_DEFINE_UNIFORM_VECTOR(float, 3, u_lightpos);
+MBGL_DEFINE_UNIFORM_VECTOR(float, 3, u_lightcolor);
+MBGL_DEFINE_UNIFORM_SCALAR(float, u_lightintensity);
+MBGL_DEFINE_UNIFORM_SCALAR(float, u_height_factor);
+} // namespace uniforms
+
+struct FillExtrusionLayoutAttributes : gl::Attributes<
+ attributes::a_pos,
+ attributes::a_normal,
+ attributes::a_edgedistance>
+{};
+
+struct FillExtrusionUniforms : gl::Uniforms<
+ uniforms::u_matrix,
+ uniforms::u_lightcolor,
+ uniforms::u_lightpos,
+ uniforms::u_lightintensity>
+{
+ static Values values(mat4,
+ const TransformState&,
+ const style::EvaluatedLight&);
+};
+
+struct FillExtrusionPatternUniforms : gl::Uniforms<
+ uniforms::u_matrix,
+ uniforms::u_pattern_tl_a,
+ uniforms::u_pattern_br_a,
+ uniforms::u_pattern_tl_b,
+ uniforms::u_pattern_br_b,
+ uniforms::u_pattern_size_a,
+ uniforms::u_pattern_size_b,
+ uniforms::u_scale_a,
+ uniforms::u_scale_b,
+ uniforms::u_mix,
+ uniforms::u_image,
+ uniforms::u_pixel_coord_upper,
+ uniforms::u_pixel_coord_lower,
+ uniforms::u_tile_units_to_pixels,
+ uniforms::u_height_factor,
+ uniforms::u_lightcolor,
+ uniforms::u_lightpos,
+ uniforms::u_lightintensity>
+{
+ static Values values(mat4,
+ const SpriteAtlasElement&,
+ const SpriteAtlasElement&,
+ const style::Faded<std::string>&,
+ const UnwrappedTileID&,
+ const TransformState&,
+ const float,
+ const style::EvaluatedLight&);
+};
+
+class FillExtrusionProgram : public Program<
+ shaders::fill_extrusion,
+ gl::Triangle,
+ FillExtrusionLayoutAttributes,
+ FillExtrusionUniforms,
+ style::FillExtrusionPaintProperties>
+{
+public:
+ using Program::Program;
+
+ static LayoutVertex layoutVertex(Point<int16_t> p, double nx, double ny, double nz, unsigned short t, uint16_t e) {
+ const auto factor = pow(2, 13);
+
+ return LayoutVertex {
+ {{
+ p.x,
+ p.y
+ }},
+ {{
+ // Multiply normal vector components by 2^14 to pack them into integers
+ // We pack a bool (`t`) into the x component indicating whether it is an upper or lower vertex
+ static_cast<int16_t>(floor(nx * factor) * 2 + t),
+ static_cast<int16_t>(ny * factor * 2),
+ static_cast<int16_t>(nz * factor * 2)
+
+ }},
+ {{
+ // The edgedistance attribute is used for wrapping fill_extrusion patterns
+ e
+ }}
+ };
+ }
+};
+
+class FillExtrusionPatternProgram : public Program<
+ shaders::fill_extrusion_pattern,
+ gl::Triangle,
+ FillExtrusionLayoutAttributes,
+ FillExtrusionPatternUniforms,
+ style::FillExtrusionPaintProperties>
+{
+public:
+ using Program::Program;
+};
+
+using FillExtrusionLayoutVertex = FillExtrusionProgram::LayoutVertex;
+using FillExtrusionAttributes = FillExtrusionProgram::Attributes;
+
+} // namespace mbgl
diff --git a/src/mbgl/programs/fill_program.hpp b/src/mbgl/programs/fill_program.hpp
index 84ca2748d6..bbf5c39fb9 100644
--- a/src/mbgl/programs/fill_program.hpp
+++ b/src/mbgl/programs/fill_program.hpp
@@ -24,15 +24,6 @@ namespace style {
template <class> class Faded;
} // namespace style
-namespace uniforms {
-MBGL_DEFINE_UNIFORM_SCALAR(Size, u_world);
-MBGL_DEFINE_UNIFORM_SCALAR(float, u_scale_a);
-MBGL_DEFINE_UNIFORM_SCALAR(float, u_scale_b);
-MBGL_DEFINE_UNIFORM_SCALAR(float, u_tile_units_to_pixels);
-MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_pixel_coord_upper);
-MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_pixel_coord_lower);
-} // namespace uniforms
-
struct FillLayoutAttributes : gl::Attributes<
attributes::a_pos>
{};
diff --git a/src/mbgl/programs/programs.hpp b/src/mbgl/programs/programs.hpp
index dbf5b9e87d..ff6b1cd505 100644
--- a/src/mbgl/programs/programs.hpp
+++ b/src/mbgl/programs/programs.hpp
@@ -1,7 +1,9 @@
#pragma once
#include <mbgl/programs/circle_program.hpp>
+#include <mbgl/programs/extrusion_texture_program.hpp>
#include <mbgl/programs/fill_program.hpp>
+#include <mbgl/programs/fill_extrusion_program.hpp>
#include <mbgl/programs/line_program.hpp>
#include <mbgl/programs/raster_program.hpp>
#include <mbgl/programs/symbol_program.hpp>
@@ -15,7 +17,10 @@ class Programs {
public:
Programs(gl::Context& context, const ProgramParameters& programParameters)
: circle(context, programParameters),
+ extrusionTexture(context, programParameters),
fill(context, programParameters),
+ fillExtrusion(context, programParameters),
+ fillExtrusionPattern(context, programParameters),
fillPattern(context, programParameters),
fillOutline(context, programParameters),
fillOutlinePattern(context, programParameters),
@@ -31,7 +36,10 @@ public:
}
CircleProgram circle;
+ ExtrusionTextureProgram extrusionTexture;
FillProgram fill;
+ FillExtrusionProgram fillExtrusion;
+ FillExtrusionPatternProgram fillExtrusionPattern;
FillPatternProgram fillPattern;
FillOutlineProgram fillOutline;
FillOutlinePatternProgram fillOutlinePattern;
diff --git a/src/mbgl/programs/uniforms.hpp b/src/mbgl/programs/uniforms.hpp
index e0c5a0d361..972405d5a9 100644
--- a/src/mbgl/programs/uniforms.hpp
+++ b/src/mbgl/programs/uniforms.hpp
@@ -17,6 +17,8 @@ MBGL_DEFINE_UNIFORM_SCALAR(float, u_zoom);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_pitch);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_bearing);
+MBGL_DEFINE_UNIFORM_SCALAR(Size, u_world);
+
MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_extrude_scale);
MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_pattern_tl_a);
@@ -25,9 +27,14 @@ MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_pattern_tl_b);
MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_pattern_br_b);
MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_pattern_size_a);
MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_pattern_size_b);
+MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_pixel_coord_upper);
+MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_pixel_coord_lower);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_mix);
MBGL_DEFINE_UNIFORM_SCALAR(gl::TextureUnit, u_image);
+MBGL_DEFINE_UNIFORM_SCALAR(float, u_scale_a);
+MBGL_DEFINE_UNIFORM_SCALAR(float, u_scale_b);
+MBGL_DEFINE_UNIFORM_SCALAR(float, u_tile_units_to_pixels);
} // namespace uniforms
} // namespace mbgl
diff --git a/src/mbgl/renderer/circle_bucket.hpp b/src/mbgl/renderer/circle_bucket.hpp
index 855565ebf4..b048fd7675 100644
--- a/src/mbgl/renderer/circle_bucket.hpp
+++ b/src/mbgl/renderer/circle_bucket.hpp
@@ -22,6 +22,7 @@ public:
bool hasData() const override;
void upload(gl::Context&) override;
+
void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) override;
float getQueryRadius(const RenderLayer&) const override;
diff --git a/src/mbgl/renderer/fill_extrusion_bucket.cpp b/src/mbgl/renderer/fill_extrusion_bucket.cpp
new file mode 100644
index 0000000000..2b352ab66a
--- /dev/null
+++ b/src/mbgl/renderer/fill_extrusion_bucket.cpp
@@ -0,0 +1,177 @@
+#include <mbgl/renderer/fill_extrusion_bucket.hpp>
+#include <mbgl/renderer/painter.hpp>
+#include <mbgl/programs/fill_extrusion_program.hpp>
+#include <mbgl/renderer/bucket_parameters.hpp>
+#include <mbgl/style/layers/fill_extrusion_layer_impl.hpp>
+#include <mbgl/renderer/render_fill_extrusion_layer.hpp>
+#include <mbgl/util/math.hpp>
+#include <mbgl/util/constants.hpp>
+
+#include <mapbox/earcut.hpp>
+
+#include <cassert>
+
+namespace mapbox {
+namespace util {
+template <>
+struct nth<0, mbgl::GeometryCoordinate> {
+ static int64_t get(const mbgl::GeometryCoordinate& t) {
+ return t.x;
+ };
+};
+
+template <>
+struct nth<1, mbgl::GeometryCoordinate> {
+ static int64_t get(const mbgl::GeometryCoordinate& t) {
+ return t.y;
+ };
+};
+} // namespace util
+} // namespace mapbox
+
+namespace mbgl {
+
+using namespace style;
+
+struct GeometryTooLongException : std::exception {};
+
+FillExtrusionBucket::FillExtrusionBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) {
+ for (const auto& layer : layers) {
+ paintPropertyBinders.emplace(std::piecewise_construct,
+ std::forward_as_tuple(layer->getID()),
+ std::forward_as_tuple(
+ layer->as<RenderFillExtrusionLayer>()->evaluated,
+ parameters.tileID.overscaledZ));
+ }
+}
+
+void FillExtrusionBucket::addFeature(const GeometryTileFeature& feature,
+ const GeometryCollection& geometry) {
+ for (auto& polygon : classifyRings(geometry)) {
+ // Optimize polygons with many interior rings for earcut tesselation.
+ limitHoles(polygon, 500);
+
+ std::size_t totalVertices = 0;
+
+ for (const auto& ring : polygon) {
+ totalVertices += ring.size();
+ if (totalVertices > std::numeric_limits<uint16_t>::max())
+ throw GeometryTooLongException();
+ }
+
+ if (totalVertices == 0) continue;
+
+ std::vector<uint32_t> flatIndices;
+ flatIndices.reserve(totalVertices);
+
+ std::size_t startVertices = vertices.vertexSize();
+
+ if (triangleSegments.empty() ||
+ triangleSegments.back().vertexLength + (5 * (totalVertices - 1) + 1) >
+ std::numeric_limits<uint16_t>::max()) {
+ triangleSegments.emplace_back(startVertices, triangles.indexSize());
+ }
+
+ auto& triangleSegment = triangleSegments.back();
+ assert(triangleSegment.vertexLength <= std::numeric_limits<uint16_t>::max());
+ uint16_t triangleIndex = triangleSegment.vertexLength;
+
+ assert(triangleIndex + (5 * (totalVertices - 1) + 1) <=
+ std::numeric_limits<uint16_t>::max());
+
+ for (const auto& ring : polygon) {
+ std::size_t nVertices = ring.size();
+
+ if (nVertices == 0)
+ continue;
+
+ auto edgeDistance = 0;
+
+ for (uint32_t i = 0; i < nVertices; i++) {
+ const auto& p1 = ring[i];
+
+ vertices.emplace_back(
+ FillExtrusionProgram::layoutVertex(p1, 0, 0, 1, 1, edgeDistance));
+ flatIndices.emplace_back(triangleIndex);
+ triangleIndex++;
+
+ if (i != 0) {
+ const auto& p2 = ring[i - 1];
+
+ const auto d1 = convertPoint<double>(p1);
+ const auto d2 = convertPoint<double>(p2);
+
+ const Point<double> perp = util::unit(util::perp(d1 - d2));
+
+ vertices.emplace_back(
+ FillExtrusionProgram::layoutVertex(p1, perp.x, perp.y, 0, 0, edgeDistance));
+ vertices.emplace_back(
+ FillExtrusionProgram::layoutVertex(p1, perp.x, perp.y, 0, 1, edgeDistance));
+
+ edgeDistance += util::dist<int16_t>(d1, d2);
+
+ vertices.emplace_back(
+ FillExtrusionProgram::layoutVertex(p2, perp.x, perp.y, 0, 0, edgeDistance));
+ vertices.emplace_back(
+ FillExtrusionProgram::layoutVertex(p2, perp.x, perp.y, 0, 1, edgeDistance));
+
+ triangles.emplace_back(triangleIndex, triangleIndex + 1, triangleIndex + 2);
+ triangles.emplace_back(triangleIndex + 1, triangleIndex + 2, triangleIndex + 3);
+ triangleIndex += 4;
+ triangleSegment.vertexLength += 4;
+ triangleSegment.indexLength += 6;
+ }
+ }
+ }
+
+ std::vector<uint32_t> indices = mapbox::earcut(polygon);
+
+ std::size_t nIndices = indices.size();
+ assert(nIndices % 3 == 0);
+
+ for (uint32_t i = 0; i < nIndices; i += 3) {
+ triangles.emplace_back(flatIndices[indices[i]], flatIndices[indices[i + 1]],
+ flatIndices[indices[i + 2]]);
+ }
+
+ triangleSegment.vertexLength += totalVertices;
+ triangleSegment.indexLength += nIndices;
+ }
+
+ for (auto& pair : paintPropertyBinders) {
+ pair.second.populateVertexVectors(feature, vertices.vertexSize());
+ }
+}
+
+void FillExtrusionBucket::upload(gl::Context& context) {
+ vertexBuffer = context.createVertexBuffer(std::move(vertices));
+ indexBuffer = context.createIndexBuffer(std::move(triangles));
+
+ for (auto& pair : paintPropertyBinders) {
+ pair.second.upload(context);
+ }
+
+ uploaded = true;
+}
+
+void FillExtrusionBucket::render(Painter& painter,
+ PaintParameters& parameters,
+ const RenderLayer& layer,
+ const RenderTile& tile) {
+ painter.renderFillExtrusion(parameters, *this, *layer.as<RenderFillExtrusionLayer>(), tile);
+}
+
+bool FillExtrusionBucket::hasData() const {
+ return !triangleSegments.empty();
+}
+
+float FillExtrusionBucket::getQueryRadius(const RenderLayer& layer) const {
+ if (!layer.is<RenderFillExtrusionLayer>()) {
+ return 0;
+ }
+
+ const std::array<float, 2>& translate = layer.as<RenderFillExtrusionLayer>()->evaluated.get<FillExtrusionTranslate>();
+ return util::length(translate[0], translate[1]);
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/fill_extrusion_bucket.hpp b/src/mbgl/renderer/fill_extrusion_bucket.hpp
new file mode 100644
index 0000000000..c54805d743
--- /dev/null
+++ b/src/mbgl/renderer/fill_extrusion_bucket.hpp
@@ -0,0 +1,38 @@
+#pragma once
+
+#include <mbgl/renderer/bucket.hpp>
+#include <mbgl/tile/geometry_tile_data.hpp>
+#include <mbgl/gl/vertex_buffer.hpp>
+#include <mbgl/gl/index_buffer.hpp>
+#include <mbgl/gl/segment.hpp>
+#include <mbgl/programs/fill_extrusion_program.hpp>
+#include <mbgl/style/layers/fill_extrusion_layer_properties.hpp>
+
+namespace mbgl {
+
+class BucketParameters;
+
+class FillExtrusionBucket : public Bucket {
+public:
+ FillExtrusionBucket(const BucketParameters&, const std::vector<const RenderLayer*>&);
+
+ void addFeature(const GeometryTileFeature&,
+ const GeometryCollection&) override;
+ bool hasData() const override;
+
+ void upload(gl::Context&) override;
+ void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) override;
+
+ float getQueryRadius(const RenderLayer&) const override;
+
+ gl::VertexVector<FillExtrusionLayoutVertex> vertices;
+ gl::IndexVector<gl::Triangles> triangles;
+ gl::SegmentVector<FillExtrusionAttributes> triangleSegments;
+
+ optional<gl::VertexBuffer<FillExtrusionLayoutVertex>> vertexBuffer;
+ optional<gl::IndexBuffer<gl::Triangles>> indexBuffer;
+
+ std::unordered_map<std::string, FillExtrusionProgram::PaintPropertyBinders> paintPropertyBinders;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/painter.cpp b/src/mbgl/renderer/painter.cpp
index 0c4915d3e9..36dd4a793f 100644
--- a/src/mbgl/renderer/painter.cpp
+++ b/src/mbgl/renderer/painter.cpp
@@ -16,6 +16,7 @@
#include <mbgl/renderer/render_background_layer.hpp>
#include <mbgl/renderer/render_custom_layer.hpp>
#include <mbgl/style/layers/custom_layer_impl.hpp>
+#include <mbgl/renderer/render_fill_extrusion_layer.hpp>
#include <mbgl/sprite/sprite_atlas.hpp>
#include <mbgl/geometry/line_atlas.hpp>
@@ -53,7 +54,7 @@ static gl::VertexVector<FillLayoutVertex> tileVertices() {
return result;
}
-static gl::IndexVector<gl::Triangles> tileTriangleIndices() {
+static gl::IndexVector<gl::Triangles> quadTriangleIndices() {
gl::IndexVector<gl::Triangles> result;
result.emplace_back(0, 1, 2);
result.emplace_back(1, 2, 3);
@@ -79,6 +80,16 @@ static gl::VertexVector<RasterLayoutVertex> rasterVertices() {
return result;
}
+static gl::VertexVector<ExtrusionTextureLayoutVertex> extrusionTextureVertices() {
+ gl::VertexVector<ExtrusionTextureLayoutVertex> result;
+ result.emplace_back(ExtrusionTextureProgram::layoutVertex({ 0, 0 }));
+ result.emplace_back(ExtrusionTextureProgram::layoutVertex({ 1, 0 }));
+ result.emplace_back(ExtrusionTextureProgram::layoutVertex({ 0, 1 }));
+ result.emplace_back(ExtrusionTextureProgram::layoutVertex({ 1, 1 }));
+ return result;
+}
+
+
Painter::Painter(gl::Context& context_,
const TransformState& state_,
float pixelRatio,
@@ -87,12 +98,14 @@ Painter::Painter(gl::Context& context_,
state(state_),
tileVertexBuffer(context.createVertexBuffer(tileVertices())),
rasterVertexBuffer(context.createVertexBuffer(rasterVertices())),
- tileTriangleIndexBuffer(context.createIndexBuffer(tileTriangleIndices())),
+ extrusionTextureVertexBuffer(context.createVertexBuffer(extrusionTextureVertices())),
+ quadTriangleIndexBuffer(context.createIndexBuffer(quadTriangleIndices())),
tileBorderIndexBuffer(context.createIndexBuffer(tileLineStripIndices())) {
tileTriangleSegments.emplace_back(0, 0, 4, 6);
tileBorderSegments.emplace_back(0, 0, 4, 5);
rasterSegments.emplace_back(0, 0, 4, 6);
+ extrusionTextureSegments.emplace_back(0, 0, 4, 6);
programs = std::make_unique<Programs>(context,
ProgramParameters{ pixelRatio, false, programCacheDir });
@@ -131,12 +144,18 @@ void Painter::render(const Style& style, const FrameData& frame_, View& view, Sp
spriteAtlas = style.spriteAtlas.get();
lineAtlas = style.lineAtlas.get();
+ evaluatedLight = style.evaluatedLight;
+
RenderData renderData = style.getRenderData(frame.debugOptions, state.getAngle());
const std::vector<RenderItem>& order = renderData.order;
const std::unordered_set<Source*>& sources = renderData.sources;
// Update the default matrices to the current viewport dimensions.
state.getProjMatrix(projMatrix);
+ // Calculate a second projection matrix with the near plane clipped to 100 so as
+ // not to waste lots of depth buffer precision on very close empty space, for layer
+ // types (fill-extrusion) that use the depth buffer to emulate real-world space.
+ state.getProjMatrix(nearClippedProjMatrix, 100);
pixelsToGLUnits = {{ 2.0f / state.getSize().width, -2.0f / state.getSize().height }};
if (state.getViewportMode() == ViewportMode::FlippedY) {
@@ -160,8 +179,11 @@ void Painter::render(const Style& style, const FrameData& frame_, View& view, Sp
annotationSpriteAtlas.upload(context, 0);
for (const auto& item : order) {
- if (item.bucket && item.bucket->needsUpload()) {
- item.bucket->upload(context);
+ for (const auto& tileRef : item.tiles) {
+ const auto& bucket = tileRef.get().tile.getBucket(item.layer);
+ if (bucket && bucket->needsUpload()) {
+ bucket->upload(context);
+ }
}
}
}
@@ -187,7 +209,7 @@ void Painter::render(const Style& style, const FrameData& frame_, View& view, Sp
// Update all clipping IDs.
algorithm::ClipIDGenerator generator;
for (const auto& source : sources) {
- source->baseImpl->startRender(generator, projMatrix, state);
+ source->baseImpl->startRender(generator, projMatrix, nearClippedProjMatrix, state);
}
MBGL_DEBUG_GROUP(context, "clipping masks");
@@ -208,7 +230,6 @@ void Painter::render(const Style& style, const FrameData& frame_, View& view, Sp
// Actually render the layers
if (debug::renderTree) { Log::Info(Event::Render, "{"); indent++; }
- // TODO: Correctly compute the number of layers recursively beforehand.
depthRangeSize = 1 - (order.size() + 2) * numSublayers * depthEpsilon;
// - OPAQUE PASS -------------------------------------------------------------------------------
@@ -302,9 +323,50 @@ void Painter::renderPass(PaintParameters& parameters,
// the viewport or Framebuffer.
parameters.view.bind();
context.setDirtyState();
+ } else if (layer.is<RenderFillExtrusionLayer>()) {
+ const auto size = context.viewport.getCurrentValue().size;
+
+ OffscreenTexture texture(context, size);
+ texture.bindRenderbuffers(1);
+
+ context.setStencilMode(gl::StencilMode::disabled());
+ context.setDepthMode(depthModeForSublayer(0, gl::DepthMode::ReadWrite));
+ context.clear(Color{ 0.0f, 0.0f, 0.0f, 0.0f }, 1.0f, {});
+
+ for (auto& tileRef : item.tiles) {
+ auto& tile = tileRef.get();
+
+ MBGL_DEBUG_GROUP(context, layer.baseImpl.id + " - " + util::toString(tile.id));
+ auto bucket = tile.tile.getBucket(layer);
+ bucket->render(*this, parameters, layer, tile);
+ }
+
+ parameters.view.bind();
+
+ mat4 viewportMat;
+ matrix::ortho(viewportMat, 0, size.width, size.height, 0, 0, 1);
+
+ const PaintProperties<>::Evaluated properties{};
+
+ parameters.programs.extrusionTexture.draw(
+ context, gl::Triangles(), gl::DepthMode::disabled(), gl::StencilMode::disabled(),
+ colorModeForRenderPass(),
+ ExtrusionTextureProgram::UniformValues{
+ uniforms::u_matrix::Value{ viewportMat }, uniforms::u_world::Value{ size },
+ uniforms::u_image::Value{ 1 },
+ uniforms::u_opacity::Value{
+ layer.as<RenderFillExtrusionLayer>()->evaluated.get<FillExtrusionOpacity>() } },
+ extrusionTextureVertexBuffer, quadTriangleIndexBuffer,
+ extrusionTextureSegments,
+ ExtrusionTextureProgram::PaintPropertyBinders{ properties, 0 }, properties,
+ state.getZoom());
} else {
- MBGL_DEBUG_GROUP(context, layer.baseImpl.id + " - " + util::toString(item.tile->id));
- item.bucket->render(*this, parameters, layer, *item.tile);
+ for (auto& tileRef : item.tiles) {
+ auto& tile = tileRef.get();
+ MBGL_DEBUG_GROUP(context, layer.baseImpl.id + " - " + util::toString(tile.id));
+ auto bucket = tile.tile.getBucket(layer);
+ bucket->render(*this, parameters, layer, tile);
+ }
}
}
diff --git a/src/mbgl/renderer/painter.hpp b/src/mbgl/renderer/painter.hpp
index 9c6dd4505f..7706d2d451 100644
--- a/src/mbgl/renderer/painter.hpp
+++ b/src/mbgl/renderer/painter.hpp
@@ -12,6 +12,7 @@
#include <mbgl/programs/debug_program.hpp>
#include <mbgl/programs/program_parameters.hpp>
#include <mbgl/programs/fill_program.hpp>
+#include <mbgl/programs/extrusion_texture_program.hpp>
#include <mbgl/programs/raster_program.hpp>
#include <mbgl/style/style.hpp>
@@ -37,12 +38,14 @@ class Tile;
class DebugBucket;
class FillBucket;
+class FillExtrusionBucket;
class LineBucket;
class CircleBucket;
class SymbolBucket;
class RasterBucket;
class RenderFillLayer;
+class RenderFillExtrusionLayer;
class RenderLineLayer;
class RenderCircleLayer;
class RenderSymbolLayer;
@@ -82,6 +85,7 @@ public:
void renderClippingMask(const UnwrappedTileID&, const ClipID&);
void renderTileDebug(const RenderTile&);
void renderFill(PaintParameters&, FillBucket&, const RenderFillLayer&, const RenderTile&);
+ void renderFillExtrusion(PaintParameters&, FillExtrusionBucket&, const RenderFillExtrusionLayer&, const RenderTile&);
void renderLine(PaintParameters&, LineBucket&, const RenderLineLayer&, const RenderTile&);
void renderCircle(PaintParameters&, CircleBucket&, const RenderCircleLayer&, const RenderTile&);
void renderSymbol(PaintParameters&, SymbolBucket&, const RenderSymbolLayer&, const RenderTile&);
@@ -126,6 +130,7 @@ private:
gl::Context& context;
mat4 projMatrix;
+ mat4 nearClippedProjMatrix;
std::array<float, 2> pixelsToGLUnits;
@@ -152,6 +157,8 @@ private:
GlyphAtlas* glyphAtlas = nullptr;
LineAtlas* lineAtlas = nullptr;
+ style::EvaluatedLight evaluatedLight;
+
FrameHistory frameHistory;
std::unique_ptr<Programs> programs;
@@ -161,13 +168,15 @@ private:
gl::VertexBuffer<FillLayoutVertex> tileVertexBuffer;
gl::VertexBuffer<RasterLayoutVertex> rasterVertexBuffer;
+ gl::VertexBuffer<ExtrusionTextureLayoutVertex> extrusionTextureVertexBuffer;
- gl::IndexBuffer<gl::Triangles> tileTriangleIndexBuffer;
+ gl::IndexBuffer<gl::Triangles> quadTriangleIndexBuffer;
gl::IndexBuffer<gl::LineStrip> tileBorderIndexBuffer;
gl::SegmentVector<FillAttributes> tileTriangleSegments;
gl::SegmentVector<DebugAttributes> tileBorderSegments;
gl::SegmentVector<RasterAttributes> rasterSegments;
+ gl::SegmentVector<ExtrusionTextureAttributes> extrusionTextureSegments;
};
} // namespace mbgl
diff --git a/src/mbgl/renderer/painter_background.cpp b/src/mbgl/renderer/painter_background.cpp
index ac16627246..7cd9cbac5f 100644
--- a/src/mbgl/renderer/painter_background.cpp
+++ b/src/mbgl/renderer/painter_background.cpp
@@ -49,7 +49,7 @@ void Painter::renderBackground(PaintParameters& parameters, const RenderBackgrou
state
),
tileVertexBuffer,
- tileTriangleIndexBuffer,
+ quadTriangleIndexBuffer,
tileTriangleSegments,
paintAttibuteData,
properties,
@@ -69,7 +69,7 @@ void Painter::renderBackground(PaintParameters& parameters, const RenderBackgrou
uniforms::u_world::Value{ context.viewport.getCurrentValue().size },
},
tileVertexBuffer,
- tileTriangleIndexBuffer,
+ quadTriangleIndexBuffer,
tileTriangleSegments,
paintAttibuteData,
properties,
diff --git a/src/mbgl/renderer/painter_clipping.cpp b/src/mbgl/renderer/painter_clipping.cpp
index 70df9837e8..0d3b5f1504 100644
--- a/src/mbgl/renderer/painter_clipping.cpp
+++ b/src/mbgl/renderer/painter_clipping.cpp
@@ -26,7 +26,7 @@ void Painter::renderClippingMask(const UnwrappedTileID& tileID, const ClipID& cl
uniforms::u_world::Value{ context.viewport.getCurrentValue().size },
},
tileVertexBuffer,
- tileTriangleIndexBuffer,
+ quadTriangleIndexBuffer,
tileTriangleSegments,
paintAttibuteData,
properties,
diff --git a/src/mbgl/renderer/painter_fill_extrusion.cpp b/src/mbgl/renderer/painter_fill_extrusion.cpp
new file mode 100644
index 0000000000..af98cae569
--- /dev/null
+++ b/src/mbgl/renderer/painter_fill_extrusion.cpp
@@ -0,0 +1,87 @@
+#include <mbgl/renderer/painter.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
+#include <mbgl/renderer/fill_extrusion_bucket.hpp>
+#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/renderer/render_fill_extrusion_layer.hpp>
+#include <mbgl/style/layers/fill_extrusion_layer_impl.hpp>
+#include <mbgl/sprite/sprite_atlas.hpp>
+#include <mbgl/programs/programs.hpp>
+#include <mbgl/programs/fill_extrusion_program.hpp>
+#include <mbgl/util/constants.hpp>
+#include <mbgl/util/convert.hpp>
+
+namespace mbgl {
+
+using namespace style;
+
+void Painter::renderFillExtrusion(PaintParameters& parameters,
+ FillExtrusionBucket& bucket,
+ const RenderFillExtrusionLayer& layer,
+ const RenderTile& tile) {
+ const FillExtrusionPaintProperties::Evaluated& properties = layer.evaluated;
+
+ if (pass == RenderPass::Opaque) {
+ return;
+ }
+
+ if (!properties.get<FillExtrusionPattern>().from.empty()) {
+ optional<SpriteAtlasElement> imagePosA =
+ spriteAtlas->getPattern(properties.get<FillExtrusionPattern>().from);
+ optional<SpriteAtlasElement> imagePosB =
+ spriteAtlas->getPattern(properties.get<FillExtrusionPattern>().to);
+
+ if (!imagePosA || !imagePosB) {
+ return;
+ }
+
+ spriteAtlas->bind(true, context, 0);
+
+ parameters.programs.fillExtrusionPattern.draw(
+ context,
+ gl::Triangles(),
+ depthModeForSublayer(0, gl::DepthMode::ReadWrite),
+ gl::StencilMode::disabled(),
+ colorModeForRenderPass(),
+ FillExtrusionPatternUniforms::values(
+ tile.translatedClipMatrix(properties.get<FillExtrusionTranslate>(),
+ properties.get<FillExtrusionTranslateAnchor>(),
+ state),
+ *imagePosA,
+ *imagePosB,
+ properties.get<FillExtrusionPattern>(),
+ tile.id,
+ state,
+ -std::pow(2, tile.id.canonical.z) / util::tileSize / 8.0f,
+ evaluatedLight
+ ),
+ *bucket.vertexBuffer,
+ *bucket.indexBuffer,
+ bucket.triangleSegments,
+ bucket.paintPropertyBinders.at(layer.getID()),
+ properties,
+ state.getZoom());
+
+ } else {
+ parameters.programs.fillExtrusion.draw(
+ context,
+ gl::Triangles(),
+ depthModeForSublayer(0, gl::DepthMode::ReadWrite),
+ gl::StencilMode::disabled(),
+ colorModeForRenderPass(),
+ FillExtrusionUniforms::values(
+ tile.translatedClipMatrix(properties.get<FillExtrusionTranslate>(),
+ properties.get<FillExtrusionTranslateAnchor>(),
+ state),
+ state,
+ evaluatedLight
+ ),
+ *bucket.vertexBuffer,
+ *bucket.indexBuffer,
+ bucket.triangleSegments,
+ bucket.paintPropertyBinders.at(layer.getID()),
+ properties,
+ state.getZoom());
+ };
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/painter_raster.cpp b/src/mbgl/renderer/painter_raster.cpp
index 31f10ed3ba..fbe025b5b0 100644
--- a/src/mbgl/renderer/painter_raster.cpp
+++ b/src/mbgl/renderer/painter_raster.cpp
@@ -77,7 +77,7 @@ void Painter::renderRaster(PaintParameters& parameters,
uniforms::u_tl_parent::Value{ std::array<float, 2> {{ 0.0f, 0.0f }} },
},
rasterVertexBuffer,
- tileTriangleIndexBuffer,
+ quadTriangleIndexBuffer,
rasterSegments,
paintAttributeData,
properties,
diff --git a/src/mbgl/renderer/render_fill_extrusion_layer.cpp b/src/mbgl/renderer/render_fill_extrusion_layer.cpp
index a378f12cf0..8df0d36900 100644
--- a/src/mbgl/renderer/render_fill_extrusion_layer.cpp
+++ b/src/mbgl/renderer/render_fill_extrusion_layer.cpp
@@ -1,26 +1,53 @@
#include <mbgl/renderer/render_fill_extrusion_layer.hpp>
-#include <mbgl/renderer/bucket.hpp>
+#include <mbgl/renderer/fill_extrusion_bucket.hpp>
#include <mbgl/style/layers/fill_extrusion_layer_impl.hpp>
+#include <mbgl/geometry/feature_index.hpp>
+#include <mbgl/util/math.hpp>
+#include <mbgl/util/intersection_tests.hpp>
namespace mbgl {
RenderFillExtrusionLayer::RenderFillExtrusionLayer(const style::FillExtrusionLayer::Impl& _impl)
- : RenderLayer(style::LayerType::FillExtrusion, _impl) {
+ : RenderLayer(style::LayerType::FillExtrusion, _impl),
+ impl(&_impl) {
}
std::unique_ptr<RenderLayer> RenderFillExtrusionLayer::clone() const {
return std::make_unique<RenderFillExtrusionLayer>(*this);
}
-std::unique_ptr<Bucket> RenderFillExtrusionLayer::createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const {
- return nullptr;
+std::unique_ptr<Bucket> RenderFillExtrusionLayer::createBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) const {
+ return std::make_unique<FillExtrusionBucket>(parameters, layers);
}
-void RenderFillExtrusionLayer::cascade(const style::CascadeParameters&) {
+void RenderFillExtrusionLayer::cascade(const style::CascadeParameters& parameters) {
+ unevaluated = impl->cascading.cascade(parameters, std::move(unevaluated));
}
-bool RenderFillExtrusionLayer::evaluate(const style::PropertyEvaluationParameters&) {
- return false;
+bool RenderFillExtrusionLayer::evaluate(const style::PropertyEvaluationParameters& parameters) {
+ evaluated = unevaluated.evaluate(parameters);
+
+ passes = (evaluated.get<style::FillExtrusionOpacity>() > 0) ? RenderPass::Translucent
+ : RenderPass::None;
+
+ return unevaluated.hasTransition();
+}
+
+bool RenderFillExtrusionLayer::queryIntersectsFeature(
+ const GeometryCoordinates& queryGeometry,
+ const GeometryTileFeature& feature,
+ const float,
+ const float bearing,
+ const float pixelsToTileUnits) const {
+
+ auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry(
+ queryGeometry,
+ evaluated.get<style::FillExtrusionTranslate>(),
+ evaluated.get<style::FillExtrusionTranslateAnchor>(),
+ bearing,
+ pixelsToTileUnits);
+
+ return util::polygonIntersectsMultiPolygon(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries());
}
} // namespace mbgl
diff --git a/src/mbgl/renderer/render_fill_extrusion_layer.hpp b/src/mbgl/renderer/render_fill_extrusion_layer.hpp
index 596ba02fee..87b6ad6071 100644
--- a/src/mbgl/renderer/render_fill_extrusion_layer.hpp
+++ b/src/mbgl/renderer/render_fill_extrusion_layer.hpp
@@ -17,11 +17,20 @@ public:
void cascade(const style::CascadeParameters&) override;
bool evaluate(const style::PropertyEvaluationParameters&) override;
+ bool queryIntersectsFeature(
+ const GeometryCoordinates&,
+ const GeometryTileFeature&,
+ const float,
+ const float,
+ const float) const override;
+
std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override;
// Paint properties
style::FillExtrusionPaintProperties::Unevaluated unevaluated;
style::FillExtrusionPaintProperties::Evaluated evaluated;
+
+ const style::FillExtrusionLayer::Impl* const impl;
};
template <>
diff --git a/src/mbgl/renderer/render_item.hpp b/src/mbgl/renderer/render_item.hpp
index 575287d9c6..01bb263d1e 100644
--- a/src/mbgl/renderer/render_item.hpp
+++ b/src/mbgl/renderer/render_item.hpp
@@ -18,14 +18,12 @@ class Source;
class RenderItem {
public:
RenderItem(const RenderLayer& layer_,
- const RenderTile* tile_ = nullptr,
- Bucket* bucket_ = nullptr)
- : tile(tile_), bucket(bucket_), layer(layer_) {
+ std::vector<std::reference_wrapper<RenderTile>> tiles_ = {})
+ : layer(layer_), tiles(std::move(tiles_)) {
}
- const RenderTile* const tile;
- Bucket* const bucket;
const RenderLayer& layer;
+ std::vector<std::reference_wrapper<RenderTile>> tiles;
};
class RenderData {
diff --git a/src/mbgl/renderer/render_tile.cpp b/src/mbgl/renderer/render_tile.cpp
index 5c7c491be0..ce59186e61 100644
--- a/src/mbgl/renderer/render_tile.cpp
+++ b/src/mbgl/renderer/render_tile.cpp
@@ -5,11 +5,12 @@ namespace mbgl {
using namespace style;
-mat4 RenderTile::translatedMatrix(const std::array<float, 2>& translation,
- TranslateAnchorType anchor,
- const TransformState& state) const {
+mat4 RenderTile::translateVtxMatrix(const mat4& tileMatrix,
+ const std::array<float, 2>& translation,
+ TranslateAnchorType anchor,
+ const TransformState& state) const {
if (translation[0] == 0 && translation[1] == 0) {
- return matrix;
+ return tileMatrix;
}
mat4 vtxMatrix;
@@ -17,18 +18,41 @@ mat4 RenderTile::translatedMatrix(const std::array<float, 2>& translation,
if (anchor == TranslateAnchorType::Viewport) {
const double sin_a = std::sin(-state.getAngle());
const double cos_a = std::cos(-state.getAngle());
- matrix::translate(vtxMatrix, matrix,
- id.pixelsToTileUnits(translation[0] * cos_a - translation[1] * sin_a, state.getZoom()),
- id.pixelsToTileUnits(translation[0] * sin_a + translation[1] * cos_a, state.getZoom()),
- 0);
+ matrix::translate(vtxMatrix, tileMatrix,
+ id.pixelsToTileUnits(translation[0] * cos_a - translation[1] * sin_a, state.getZoom()),
+ id.pixelsToTileUnits(translation[0] * sin_a + translation[1] * cos_a, state.getZoom()),
+ 0);
} else {
- matrix::translate(vtxMatrix, matrix,
- id.pixelsToTileUnits(translation[0], state.getZoom()),
- id.pixelsToTileUnits(translation[1], state.getZoom()),
- 0);
+ matrix::translate(vtxMatrix, tileMatrix,
+ id.pixelsToTileUnits(translation[0], state.getZoom()),
+ id.pixelsToTileUnits(translation[1], state.getZoom()),
+ 0);
}
return vtxMatrix;
}
+mat4 RenderTile::translatedMatrix(const std::array<float, 2>& translation,
+ TranslateAnchorType anchor,
+ const TransformState& state) const {
+ return translateVtxMatrix(matrix, translation, anchor, state);
+}
+
+mat4 RenderTile::translatedClipMatrix(const std::array<float, 2>& translation,
+ TranslateAnchorType anchor,
+ const TransformState& state) const {
+ return translateVtxMatrix(nearClippedMatrix, translation, anchor, state);
+}
+
+void RenderTile::calculateMatrices(const mat4& projMatrix,
+ const mat4& projClipMatrix,
+ const TransformState& transform) {
+ // Calculate two matrices for this tile: matrix is the standard tile matrix; nearClippedMatrix
+ // clips the near plane to 100 to save depth buffer precision
+ transform.matrixFor(matrix, id);
+ transform.matrixFor(nearClippedMatrix, id);
+ matrix::multiply(matrix, projMatrix, matrix);
+ matrix::multiply(nearClippedMatrix, projClipMatrix, nearClippedMatrix);
+}
+
} // namespace mbgl
diff --git a/src/mbgl/renderer/render_tile.hpp b/src/mbgl/renderer/render_tile.hpp
index e2e0c3d656..02e8667eec 100644
--- a/src/mbgl/renderer/render_tile.hpp
+++ b/src/mbgl/renderer/render_tile.hpp
@@ -24,11 +24,25 @@ public:
Tile& tile;
ClipID clip;
mat4 matrix;
+ mat4 nearClippedMatrix;
bool used = false;
mat4 translatedMatrix(const std::array<float, 2>& translate,
style::TranslateAnchorType anchor,
const TransformState&) const;
+
+ mat4 translatedClipMatrix(const std::array<float, 2>& translate,
+ style::TranslateAnchorType anchor,
+ const TransformState&) const;
+
+ void calculateMatrices(const mat4& projMatrix,
+ const mat4& projClipMatrix,
+ const TransformState&);
+private:
+ mat4 translateVtxMatrix(const mat4& tileMatrix,
+ const std::array<float, 2>& translation,
+ style::TranslateAnchorType anchor,
+ const TransformState& state) const;
};
} // namespace mbgl
diff --git a/src/mbgl/shaders/extrusion_texture.cpp b/src/mbgl/shaders/extrusion_texture.cpp
new file mode 100644
index 0000000000..c756db181c
--- /dev/null
+++ b/src/mbgl/shaders/extrusion_texture.cpp
@@ -0,0 +1,39 @@
+// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED.
+
+#include <mbgl/shaders/extrusion_texture.hpp>
+
+namespace mbgl {
+namespace shaders {
+
+const char* extrusion_texture::name = "extrusion_texture";
+const char* extrusion_texture::vertexSource = R"MBGL_SHADER(
+uniform mat4 u_matrix;
+uniform vec2 u_world;
+attribute vec2 a_pos;
+varying vec2 v_pos;
+
+void main() {
+ gl_Position = u_matrix * vec4(a_pos * u_world, 0, 1);
+
+ v_pos.x = a_pos.x;
+ v_pos.y = 1.0 - a_pos.y;
+}
+
+)MBGL_SHADER";
+const char* extrusion_texture::fragmentSource = R"MBGL_SHADER(
+uniform sampler2D u_image;
+uniform float u_opacity;
+varying vec2 v_pos;
+
+void main() {
+ gl_FragColor = texture2D(u_image, v_pos) * u_opacity;
+
+#ifdef OVERDRAW_INSPECTOR
+ gl_FragColor = vec4(0.0);
+#endif
+}
+
+)MBGL_SHADER";
+
+} // namespace shaders
+} // namespace mbgl
diff --git a/src/mbgl/shaders/extrusion_texture.hpp b/src/mbgl/shaders/extrusion_texture.hpp
new file mode 100644
index 0000000000..05e54c5499
--- /dev/null
+++ b/src/mbgl/shaders/extrusion_texture.hpp
@@ -0,0 +1,16 @@
+// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED.
+
+#pragma once
+
+namespace mbgl {
+namespace shaders {
+
+class extrusion_texture {
+public:
+ static const char* name;
+ static const char* vertexSource;
+ static const char* fragmentSource;
+};
+
+} // namespace shaders
+} // namespace mbgl
diff --git a/src/mbgl/shaders/fill_extrusion.cpp b/src/mbgl/shaders/fill_extrusion.cpp
new file mode 100644
index 0000000000..ea1c80545e
--- /dev/null
+++ b/src/mbgl/shaders/fill_extrusion.cpp
@@ -0,0 +1,98 @@
+// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED.
+
+#include <mbgl/shaders/fill_extrusion.hpp>
+
+namespace mbgl {
+namespace shaders {
+
+const char* fill_extrusion::name = "fill_extrusion";
+const char* fill_extrusion::vertexSource = R"MBGL_SHADER(
+uniform mat4 u_matrix;
+uniform vec3 u_lightcolor;
+uniform lowp vec3 u_lightpos;
+uniform lowp float u_lightintensity;
+
+attribute vec2 a_pos;
+attribute vec3 a_normal;
+attribute float a_edgedistance;
+
+varying vec4 v_color;
+
+uniform lowp float a_base_t;
+attribute lowp vec2 a_base;
+varying lowp float base;
+uniform lowp float a_height_t;
+attribute lowp vec2 a_height;
+varying lowp float height;
+
+uniform lowp float a_color_t;
+attribute highp vec4 a_color;
+varying highp vec4 color;
+
+void main() {
+ base = unpack_mix_vec2(a_base, a_base_t);
+ height = unpack_mix_vec2(a_height, a_height_t);
+ color = unpack_mix_vec4(a_color, a_color_t);
+
+ base = max(0.0, base);
+ height = max(0.0, height);
+
+ float ed = a_edgedistance; // use each attrib in order to not trip a VAO assert
+ float t = mod(a_normal.x, 2.0);
+
+ gl_Position = u_matrix * vec4(a_pos, t > 0.0 ? height : base, 1);
+
+ // Relative luminance (how dark/bright is the surface color?)
+ float colorvalue = color.r * 0.2126 + color.g * 0.7152 + color.b * 0.0722;
+
+ v_color = vec4(0.0, 0.0, 0.0, 1.0);
+
+ // Add slight ambient lighting so no extrusions are totally black
+ vec4 ambientlight = vec4(0.03, 0.03, 0.03, 1.0);
+ color += ambientlight;
+
+ // Calculate cos(theta), where theta is the angle between surface normal and diffuse light ray
+ float directional = clamp(dot(a_normal / 16384.0, u_lightpos), 0.0, 1.0);
+
+ // Adjust directional so that
+ // the range of values for highlight/shading is narrower
+ // with lower light intensity
+ // and with lighter/brighter surface colors
+ directional = mix((1.0 - u_lightintensity), max((1.0 - colorvalue + u_lightintensity), 1.0), directional);
+
+ // Add gradient along z axis of side surfaces
+ if (a_normal.y != 0.0) {
+ directional *= clamp((t + base) * pow(height / 150.0, 0.5), mix(0.7, 0.98, 1.0 - u_lightintensity), 1.0);
+ }
+
+ // Assign final color based on surface + ambient light color, diffuse light directional, and light color
+ // with lower bounds adjusted to hue of light
+ // so that shading is tinted with the complementary (opposite) color to the light color
+ v_color.r += clamp(color.r * directional * u_lightcolor.r, mix(0.0, 0.3, 1.0 - u_lightcolor.r), 1.0);
+ v_color.g += clamp(color.g * directional * u_lightcolor.g, mix(0.0, 0.3, 1.0 - u_lightcolor.g), 1.0);
+ v_color.b += clamp(color.b * directional * u_lightcolor.b, mix(0.0, 0.3, 1.0 - u_lightcolor.b), 1.0);
+}
+
+)MBGL_SHADER";
+const char* fill_extrusion::fragmentSource = R"MBGL_SHADER(
+varying vec4 v_color;
+varying lowp float base;
+varying lowp float height;
+varying highp vec4 color;
+
+void main() {
+
+
+
+
+ gl_FragColor = v_color;
+
+#ifdef OVERDRAW_INSPECTOR
+ gl_FragColor = vec4(1.0);
+#endif
+}
+
+)MBGL_SHADER";
+
+} // namespace shaders
+} // namespace mbgl
diff --git a/src/mbgl/shaders/fill_extrusion.hpp b/src/mbgl/shaders/fill_extrusion.hpp
new file mode 100644
index 0000000000..949ae57f12
--- /dev/null
+++ b/src/mbgl/shaders/fill_extrusion.hpp
@@ -0,0 +1,16 @@
+// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED.
+
+#pragma once
+
+namespace mbgl {
+namespace shaders {
+
+class fill_extrusion {
+public:
+ static const char* name;
+ static const char* vertexSource;
+ static const char* fragmentSource;
+};
+
+} // namespace shaders
+} // namespace mbgl
diff --git a/src/mbgl/shaders/fill_extrusion_pattern.cpp b/src/mbgl/shaders/fill_extrusion_pattern.cpp
new file mode 100644
index 0000000000..e2de5c20b2
--- /dev/null
+++ b/src/mbgl/shaders/fill_extrusion_pattern.cpp
@@ -0,0 +1,111 @@
+// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED.
+
+#include <mbgl/shaders/fill_extrusion_pattern.hpp>
+
+namespace mbgl {
+namespace shaders {
+
+const char* fill_extrusion_pattern::name = "fill_extrusion_pattern";
+const char* fill_extrusion_pattern::vertexSource = R"MBGL_SHADER(
+uniform mat4 u_matrix;
+uniform vec2 u_pattern_size_a;
+uniform vec2 u_pattern_size_b;
+uniform vec2 u_pixel_coord_upper;
+uniform vec2 u_pixel_coord_lower;
+uniform float u_scale_a;
+uniform float u_scale_b;
+uniform float u_tile_units_to_pixels;
+uniform float u_height_factor;
+
+uniform vec3 u_lightcolor;
+uniform lowp vec3 u_lightpos;
+uniform lowp float u_lightintensity;
+
+attribute vec2 a_pos;
+attribute vec3 a_normal;
+attribute float a_edgedistance;
+
+varying vec2 v_pos_a;
+varying vec2 v_pos_b;
+varying vec4 v_lighting;
+varying float v_directional;
+
+uniform lowp float a_base_t;
+attribute lowp vec2 a_base;
+varying lowp float base;
+uniform lowp float a_height_t;
+attribute lowp vec2 a_height;
+varying lowp float height;
+
+void main() {
+ base = unpack_mix_vec2(a_base, a_base_t);
+ height = unpack_mix_vec2(a_height, a_height_t);
+
+ base = max(0.0, base);
+ height = max(0.0, height);
+
+ float t = mod(a_normal.x, 2.0);
+ float z = t > 0.0 ? height : base;
+
+ gl_Position = u_matrix * vec4(a_pos, z, 1);
+
+ vec2 pos = a_normal.x == 1.0 && a_normal.y == 0.0 && a_normal.z == 16384.0
+ ? a_pos // extrusion top
+ : vec2(a_edgedistance, z * u_height_factor); // extrusion side
+
+ v_pos_a = get_pattern_pos(u_pixel_coord_upper, u_pixel_coord_lower, u_scale_a * u_pattern_size_a, u_tile_units_to_pixels, pos);
+ v_pos_b = get_pattern_pos(u_pixel_coord_upper, u_pixel_coord_lower, u_scale_b * u_pattern_size_b, u_tile_units_to_pixels, pos);
+
+ v_lighting = vec4(0.0, 0.0, 0.0, 1.0);
+ float directional = clamp(dot(a_normal / 16383.0, u_lightpos), 0.0, 1.0);
+ directional = mix((1.0 - u_lightintensity), max((0.5 + u_lightintensity), 1.0), directional);
+
+ if (a_normal.y != 0.0) {
+ directional *= clamp((t + base) * pow(height / 150.0, 0.5), mix(0.7, 0.98, 1.0 - u_lightintensity), 1.0);
+ }
+
+ v_lighting.rgb += clamp(directional * u_lightcolor, mix(vec3(0.0), vec3(0.3), 1.0 - u_lightcolor), vec3(1.0));
+}
+
+)MBGL_SHADER";
+const char* fill_extrusion_pattern::fragmentSource = R"MBGL_SHADER(
+uniform vec2 u_pattern_tl_a;
+uniform vec2 u_pattern_br_a;
+uniform vec2 u_pattern_tl_b;
+uniform vec2 u_pattern_br_b;
+uniform float u_mix;
+
+uniform sampler2D u_image;
+
+varying vec2 v_pos_a;
+varying vec2 v_pos_b;
+varying vec4 v_lighting;
+
+varying lowp float base;
+varying lowp float height;
+
+void main() {
+
+
+
+ vec2 imagecoord = mod(v_pos_a, 1.0);
+ vec2 pos = mix(u_pattern_tl_a, u_pattern_br_a, imagecoord);
+ vec4 color1 = texture2D(u_image, pos);
+
+ vec2 imagecoord_b = mod(v_pos_b, 1.0);
+ vec2 pos2 = mix(u_pattern_tl_b, u_pattern_br_b, imagecoord_b);
+ vec4 color2 = texture2D(u_image, pos2);
+
+ vec4 mixedColor = mix(color1, color2, u_mix);
+
+ gl_FragColor = mixedColor * v_lighting;
+
+#ifdef OVERDRAW_INSPECTOR
+ gl_FragColor = vec4(1.0);
+#endif
+}
+
+)MBGL_SHADER";
+
+} // namespace shaders
+} // namespace mbgl
diff --git a/src/mbgl/shaders/fill_extrusion_pattern.hpp b/src/mbgl/shaders/fill_extrusion_pattern.hpp
new file mode 100644
index 0000000000..e9ae001d4e
--- /dev/null
+++ b/src/mbgl/shaders/fill_extrusion_pattern.hpp
@@ -0,0 +1,16 @@
+// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED.
+
+#pragma once
+
+namespace mbgl {
+namespace shaders {
+
+class fill_extrusion_pattern {
+public:
+ static const char* name;
+ static const char* vertexSource;
+ static const char* fragmentSource;
+};
+
+} // namespace shaders
+} // namespace mbgl
diff --git a/src/mbgl/shaders/preludes.cpp b/src/mbgl/shaders/preludes.cpp
index cca0f3e3f1..95fa624e8d 100644
--- a/src/mbgl/shaders/preludes.cpp
+++ b/src/mbgl/shaders/preludes.cpp
@@ -48,8 +48,9 @@ vec4 evaluate_zoom_function_4(const vec4 value0, const vec4 value1, const vec4 v
// packed like so:
// packedValue = floor(input[0]) * 256 + input[1],
vec2 unpack_float(const float packedValue) {
- float v0 = floor(packedValue / 256.0);
- return vec2(v0, packedValue - v0 * 256.0);
+ int packedIntValue = int(packedValue);
+ int v0 = packedIntValue / 256;
+ return vec2(v0, packedIntValue - v0 * 256);
}
diff --git a/src/mbgl/style/layers/fill_extrusion_layer_impl.hpp b/src/mbgl/style/layers/fill_extrusion_layer_impl.hpp
index f6f9753460..2353bd99fe 100644
--- a/src/mbgl/style/layers/fill_extrusion_layer_impl.hpp
+++ b/src/mbgl/style/layers/fill_extrusion_layer_impl.hpp
@@ -16,8 +16,6 @@ public:
std::unique_ptr<RenderLayer> createRenderLayer() const override;
FillExtrusionPaintProperties::Cascading cascading;
-
- //style::FillExtrusionLayer::Impl* const impl;
};
} // namespace style
diff --git a/src/mbgl/style/light_impl.hpp b/src/mbgl/style/light_impl.hpp
new file mode 100644
index 0000000000..d1825090fc
--- /dev/null
+++ b/src/mbgl/style/light_impl.hpp
@@ -0,0 +1,74 @@
+#pragma once
+
+#include <mbgl/style/light.hpp>
+#include <mbgl/style/transitioning_property.hpp>
+#include <mbgl/style/cascade_parameters.hpp>
+#include <mbgl/style/property_evaluator.hpp>
+#include <mbgl/style/property_evaluation_parameters.hpp>
+#include <mbgl/util/ignore.hpp>
+
+namespace mbgl {
+namespace style {
+
+template <class TypeList>
+class Transitioning;
+
+template <class... Ps>
+class Transitioning<TypeList<Ps...>> : public IndexedTuple<
+ TypeList<Ps...>,
+ TypeList<TransitioningProperty<typename Ps::ValueType>...>>
+{
+private:
+ using Properties = TypeList<Ps...>;
+ using Raw = IndexedTuple<Properties, Properties>;
+ using Super = IndexedTuple<
+ TypeList<Ps...>,
+ TypeList<TransitioningProperty<typename Ps::ValueType>...>>;
+
+public:
+ Transitioning() = default;
+ Transitioning(const Raw& raw, Transitioning&& prior, const CascadeParameters& params)
+ : Super {
+ TransitioningProperty<typename Ps::ValueType>(
+ raw.template get<Ps>().value,
+ std::move(prior.template get<Ps>()),
+ raw.template get<Ps>().transition.reverseMerge(params.transition),
+ params.now)...
+ } {}
+
+ bool hasTransition() const {
+ bool result = false;
+ util::ignore({ result |= this->template get<Ps>().hasTransition()... });
+ return result;
+ }
+};
+
+template <class TypeList>
+class Evaluated;
+
+template <class... Ps>
+class Evaluated<TypeList<Ps...>> : public IndexedTuple<
+ TypeList<Ps...>,
+ TypeList<typename Ps::Type...>>
+{
+private:
+ using Properties = TypeList<Ps...>;
+ using TransitioningPs = Transitioning<Properties>;
+ using Super = IndexedTuple<
+ TypeList<Ps...>,
+ TypeList<typename Ps::Type...>>;
+
+public:
+ Evaluated() = default;
+ Evaluated(TransitioningPs& transitioning, const PropertyEvaluationParameters& params)
+ : Super {
+ transitioning.template get<Ps>()
+ .evaluate(PropertyEvaluator<typename Ps::Type>(params, Ps::defaultValue()), params.now)...
+ } {}
+};
+
+using TransitioningLight = Transitioning<LightProperties>;
+using EvaluatedLight = Evaluated<LightProperties>;
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/paint_property.hpp b/src/mbgl/style/paint_property.hpp
index 7f7018748c..dd8d6c0b50 100644
--- a/src/mbgl/style/paint_property.hpp
+++ b/src/mbgl/style/paint_property.hpp
@@ -1,5 +1,6 @@
#pragma once
+#include <mbgl/style/transitioning_property.hpp>
#include <mbgl/style/class_dictionary.hpp>
#include <mbgl/style/property_value.hpp>
#include <mbgl/style/data_driven_property_value.hpp>
@@ -24,68 +25,6 @@ class GeometryTileFeature;
namespace style {
template <class Value>
-class UnevaluatedPaintProperty {
-public:
- UnevaluatedPaintProperty() = default;
-
- UnevaluatedPaintProperty(Value value_,
- UnevaluatedPaintProperty<Value> prior_,
- TransitionOptions transition,
- TimePoint now)
- : begin(now + transition.delay.value_or(Duration::zero())),
- end(begin + transition.duration.value_or(Duration::zero())),
- value(std::move(value_)) {
- if (transition.isDefined()) {
- prior = { std::move(prior_) };
- }
- }
-
- template <class Evaluator>
- auto evaluate(const Evaluator& evaluator, TimePoint now) {
- auto finalValue = value.evaluate(evaluator);
- if (!prior) {
- // No prior value.
- return finalValue;
- } else if (now >= end) {
- // Transition from prior value is now complete.
- prior = {};
- return finalValue;
- } else if (value.isDataDriven()) {
- // Transitions to data-driven properties are not supported.
- // We snap immediately to the data-driven value so that, when we perform layout,
- // we see the data-driven function and can use it to populate vertex buffers.
- prior = {};
- return finalValue;
- } else if (now < begin) {
- // Transition hasn't started yet.
- return prior->get().evaluate(evaluator, now);
- } else {
- // Interpolate between recursively-calculated prior value and final.
- float t = std::chrono::duration<float>(now - begin) / (end - begin);
- return util::interpolate(prior->get().evaluate(evaluator, now), finalValue, util::DEFAULT_TRANSITION_EASE.solve(t, 0.001));
- }
- }
-
- bool hasTransition() const {
- return bool(prior);
- }
-
- bool isUndefined() const {
- return value.isUndefined();
- }
-
- const Value& getValue() const {
- return value;
- }
-
-private:
- optional<mapbox::util::recursive_wrapper<UnevaluatedPaintProperty<Value>>> prior;
- TimePoint begin;
- TimePoint end;
- Value value;
-};
-
-template <class Value>
class CascadingPaintProperty {
public:
bool isUndefined() const {
@@ -112,8 +51,8 @@ public:
transitions[klass ? ClassDictionary::Get().lookup(*klass) : ClassID::Default] = transition;
}
- template <class UnevaluatedPaintProperty>
- UnevaluatedPaintProperty cascade(const CascadeParameters& params, UnevaluatedPaintProperty prior) const {
+ template <class TransitioningProperty>
+ TransitioningProperty cascade(const CascadeParameters& params, TransitioningProperty prior) const {
TransitionOptions transition;
Value value;
@@ -131,7 +70,7 @@ public:
}
}
- return UnevaluatedPaintProperty(std::move(value),
+ return TransitioningProperty(std::move(value),
std::move(prior),
transition.reverseMerge(params.transition),
params.now);
@@ -147,7 +86,7 @@ class PaintProperty {
public:
using ValueType = PropertyValue<T>;
using CascadingType = CascadingPaintProperty<ValueType>;
- using UnevaluatedType = UnevaluatedPaintProperty<ValueType>;
+ using UnevaluatedType = TransitioningProperty<ValueType>;
using EvaluatorType = PropertyEvaluator<T>;
using EvaluatedType = T;
static constexpr bool IsDataDriven = false;
@@ -158,7 +97,7 @@ class DataDrivenPaintProperty {
public:
using ValueType = DataDrivenPropertyValue<T>;
using CascadingType = CascadingPaintProperty<ValueType>;
- using UnevaluatedType = UnevaluatedPaintProperty<ValueType>;
+ using UnevaluatedType = TransitioningProperty<ValueType>;
using EvaluatorType = DataDrivenPropertyEvaluator<T>;
using EvaluatedType = PossiblyEvaluatedPropertyValue<T>;
static constexpr bool IsDataDriven = true;
@@ -172,7 +111,7 @@ class CrossFadedPaintProperty {
public:
using ValueType = PropertyValue<T>;
using CascadingType = CascadingPaintProperty<ValueType>;
- using UnevaluatedType = UnevaluatedPaintProperty<ValueType>;
+ using UnevaluatedType = TransitioningProperty<ValueType>;
using EvaluatorType = CrossFadedPropertyEvaluator<T>;
using EvaluatedType = Faded<T>;
static constexpr bool IsDataDriven = false;
diff --git a/src/mbgl/style/parser.cpp b/src/mbgl/style/parser.cpp
index 19ea8cefee..c407e438fb 100644
--- a/src/mbgl/style/parser.cpp
+++ b/src/mbgl/style/parser.cpp
@@ -4,6 +4,7 @@
#include <mbgl/style/conversion.hpp>
#include <mbgl/style/conversion/source.hpp>
#include <mbgl/style/conversion/layer.hpp>
+#include <mbgl/style/conversion/light.hpp>
#include <mbgl/util/logging.hpp>
@@ -82,6 +83,10 @@ StyleParseResult Parser::parse(const std::string& json) {
}
}
+ if (document.HasMember("light")) {
+ parseLight(document["light"]);
+ }
+
if (document.HasMember("sources")) {
parseSources(document["sources"]);
}
@@ -107,6 +112,17 @@ StyleParseResult Parser::parse(const std::string& json) {
return nullptr;
}
+void Parser::parseLight(const JSValue& value) {
+ conversion::Error error;
+ optional<Light> converted = conversion::convert<Light>(value, error);
+ if (!converted) {
+ Log::Warning(Event::ParseStyle, error.message);
+ return;
+ }
+
+ light = *converted;
+}
+
void Parser::parseSources(const JSValue& value) {
if (!value.IsObject()) {
Log::Warning(Event::ParseStyle, "sources must be an object");
diff --git a/src/mbgl/style/parser.hpp b/src/mbgl/style/parser.hpp
index a05a0b316a..32b8a7a8bc 100644
--- a/src/mbgl/style/parser.hpp
+++ b/src/mbgl/style/parser.hpp
@@ -2,6 +2,7 @@
#include <mbgl/style/layer.hpp>
#include <mbgl/style/source.hpp>
+#include <mbgl/style/light.hpp>
#include <mbgl/util/rapidjson.hpp>
#include <mbgl/util/font_stack.hpp>
@@ -31,6 +32,8 @@ public:
std::vector<std::unique_ptr<Source>> sources;
std::vector<std::unique_ptr<Layer>> layers;
+ Light light;
+
std::string name;
LatLng latLng;
double zoom = 0;
@@ -41,6 +44,7 @@ public:
std::vector<FontStack> fontStacks() const;
private:
+ void parseLight(const JSValue&);
void parseSources(const JSValue&);
void parseLayers(const JSValue&);
void parseLayer(const std::string& id, const JSValue&, std::unique_ptr<Layer>&);
diff --git a/src/mbgl/style/source_impl.cpp b/src/mbgl/style/source_impl.cpp
index 9fabc54f7d..7a65dd1ded 100644
--- a/src/mbgl/style/source_impl.cpp
+++ b/src/mbgl/style/source_impl.cpp
@@ -57,8 +57,9 @@ void Source::Impl::invalidateTiles() {
}
void Source::Impl::startRender(algorithm::ClipIDGenerator& generator,
- const mat4& projMatrix,
- const TransformState& transform) {
+ const mat4& projMatrix,
+ const mat4& clipMatrix,
+ const TransformState& transform) {
if (type == SourceType::Vector ||
type == SourceType::GeoJSON ||
type == SourceType::Annotations) {
@@ -67,8 +68,7 @@ void Source::Impl::startRender(algorithm::ClipIDGenerator& generator,
for (auto& pair : renderTiles) {
auto& tile = pair.second;
- transform.matrixFor(tile.matrix, tile.id);
- matrix::multiply(tile.matrix, projMatrix, tile.matrix);
+ tile.calculateMatrices(projMatrix, clipMatrix, transform);
}
}
diff --git a/src/mbgl/style/source_impl.hpp b/src/mbgl/style/source_impl.hpp
index b4473cbc84..387428ec59 100644
--- a/src/mbgl/style/source_impl.hpp
+++ b/src/mbgl/style/source_impl.hpp
@@ -60,6 +60,7 @@ public:
void startRender(algorithm::ClipIDGenerator&,
const mat4& projMatrix,
+ const mat4& clipMatrix,
const TransformState&);
void finishRender(Painter&);
diff --git a/src/mbgl/style/style.cpp b/src/mbgl/style/style.cpp
index e8141c3c6b..8dfe7eac06 100644
--- a/src/mbgl/style/style.cpp
+++ b/src/mbgl/style/style.cpp
@@ -54,6 +54,7 @@ Style::Style(Scheduler& scheduler_, FileSource& fileSource_, float pixelRatio)
glyphAtlas(std::make_unique<GlyphAtlas>(Size{ 2048, 2048 }, fileSource)),
spriteAtlas(std::make_unique<SpriteAtlas>(Size{ 1024, 1024 }, pixelRatio)),
lineAtlas(std::make_unique<LineAtlas>(Size{ 256, 512 })),
+ light(std::make_unique<Light>()),
observer(&nullObserver) {
glyphAtlas->setObserver(this);
spriteAtlas->setObserver(this);
@@ -141,6 +142,7 @@ void Style::setJSON(const std::string& json) {
defaultZoom = parser.zoom;
defaultBearing = parser.bearing;
defaultPitch = parser.pitch;
+ light = std::make_unique<Light>(parser.light);
glyphAtlas->setURL(parser.glyphURL);
spriteAtlas->load(parser.spriteURL, scheduler, fileSource);
@@ -352,6 +354,8 @@ void Style::cascade(const TimePoint& timePoint, MapMode mode) {
for (const auto& layer : renderLayers) {
layer->cascade(parameters);
}
+
+ transitioningLight = TransitioningLight(*light, std::move(transitioningLight), parameters);
}
void Style::recalculate(float z, const TimePoint& timePoint, MapMode mode) {
@@ -370,7 +374,7 @@ void Style::recalculate(float z, const TimePoint& timePoint, MapMode mode) {
mode == MapMode::Continuous ? util::DEFAULT_FADE_DURATION : Duration::zero()
};
- hasPendingTransitions = false;
+ hasPendingTransitions = transitioningLight.hasTransition();
for (const auto& layer : renderLayers) {
hasPendingTransitions |= layer->evaluate(parameters);
@@ -389,6 +393,8 @@ void Style::recalculate(float z, const TimePoint& timePoint, MapMode mode) {
}
}
+ evaluatedLight = EvaluatedLight(transitioningLight, parameters);
+
// Remove the existing tiles if we didn't end up re-enabling the source.
for (const auto& source : sources) {
if (!source->baseImpl->enabled) {
@@ -509,8 +515,9 @@ RenderData Style::getRenderData(MapDebugOptions debugOptions, float angle) const
});
}
- for (auto& tileRef : sortedTiles) {
- auto& tile = tileRef.get();
+ std::vector<std::reference_wrapper<RenderTile>> sortedTilesForInsertion;
+ for (auto tileIt = sortedTiles.begin(); tileIt != sortedTiles.end(); ++tileIt) {
+ auto& tile = tileIt->get();
if (!tile.tile.isRenderable()) {
continue;
}
@@ -523,8 +530,9 @@ RenderData Style::getRenderData(MapDebugOptions debugOptions, float angle) const
// Look back through the buckets we decided to render to find out whether there is
// already a bucket from this layer that is a parent of this tile. Tiles are ordered
// by zoom level when we obtain them from getTiles().
- for (auto it = result.order.rbegin(); it != result.order.rend() && (&it->layer == layer.get()); ++it) {
- if (tile.tile.id.isChildOf(it->tile->tile.id)) {
+ for (auto it = sortedTilesForInsertion.rbegin();
+ it != sortedTilesForInsertion.rend(); ++it) {
+ if (tile.tile.id.isChildOf(it->get().tile.id)) {
skip = true;
break;
}
@@ -536,10 +544,12 @@ RenderData Style::getRenderData(MapDebugOptions debugOptions, float angle) const
auto bucket = tile.tile.getBucket(*layer);
if (bucket) {
- result.order.emplace_back(*layer, &tile, bucket);
+ sortedTilesForInsertion.emplace_back(tile);
tile.used = true;
}
}
+
+ result.order.emplace_back(*layer, std::move(sortedTilesForInsertion));
}
return result;
diff --git a/src/mbgl/style/style.hpp b/src/mbgl/style/style.hpp
index 127430a89f..26959c9fbf 100644
--- a/src/mbgl/style/style.hpp
+++ b/src/mbgl/style/style.hpp
@@ -6,6 +6,7 @@
#include <mbgl/style/layer_observer.hpp>
#include <mbgl/style/update_batch.hpp>
#include <mbgl/renderer/render_layer.hpp>
+#include <mbgl/style/light_impl.hpp>
#include <mbgl/text/glyph_atlas_observer.hpp>
#include <mbgl/sprite/sprite_atlas_observer.hpp>
#include <mbgl/map/mode.hpp>
@@ -120,6 +121,10 @@ public:
std::unique_ptr<SpriteAtlas> spriteAtlas;
std::unique_ptr<LineAtlas> lineAtlas;
+ std::unique_ptr<Light> light;
+ TransitioningLight transitioningLight;
+ EvaluatedLight evaluatedLight;
+
private:
std::vector<std::unique_ptr<Source>> sources;
std::vector<std::unique_ptr<Layer>> layers;
diff --git a/src/mbgl/style/transitioning_property.hpp b/src/mbgl/style/transitioning_property.hpp
new file mode 100644
index 0000000000..5456322b33
--- /dev/null
+++ b/src/mbgl/style/transitioning_property.hpp
@@ -0,0 +1,77 @@
+#pragma once
+
+#include <mbgl/style/property_value.hpp>
+#include <mbgl/style/data_driven_property_value.hpp>
+#include <mbgl/style/transition_options.hpp>
+#include <mbgl/util/interpolate.hpp>
+
+#include <utility>
+
+namespace mbgl {
+namespace style {
+
+template <class Value>
+class TransitioningProperty {
+public:
+ TransitioningProperty() = default;
+
+ TransitioningProperty(Value value_,
+ TransitioningProperty<Value> prior_,
+ TransitionOptions transition,
+ TimePoint now)
+ : begin(now + transition.delay.value_or(Duration::zero())),
+ end(begin + transition.duration.value_or(Duration::zero())),
+ value(std::move(value_)) {
+ if (transition.isDefined()) {
+ prior = { std::move(prior_) };
+ }
+ }
+
+ template <class Evaluator>
+ auto evaluate(const Evaluator& evaluator, TimePoint now) {
+ auto finalValue = value.evaluate(evaluator);
+ if (!prior) {
+ // No prior value.
+ return finalValue;
+ } else if (now >= end) {
+ // Transition from prior value is now complete.
+ prior = {};
+ return finalValue;
+ } else if (value.isDataDriven()) {
+ // Transitions to data-driven properties are not supported.
+ // We snap immediately to the data-driven value so that, when we perform layout,
+ // we see the data-driven function and can use it to populate vertex buffers.
+ prior = {};
+ return finalValue;
+ } else if (now < begin) {
+ // Transition hasn't started yet.
+ return prior->get().evaluate(evaluator, now);
+ } else {
+ // Interpolate between recursively-calculated prior value and final.
+ float t = std::chrono::duration<float>(now - begin) / (end - begin);
+ return util::interpolate(prior->get().evaluate(evaluator, now), finalValue,
+ util::DEFAULT_TRANSITION_EASE.solve(t, 0.001));
+ }
+ }
+
+ bool hasTransition() const {
+ return bool(prior);
+ }
+
+ bool isUndefined() const {
+ return value.isUndefined();
+ }
+
+ const Value& getValue() const {
+ return value;
+ }
+
+private:
+ optional<mapbox::util::recursive_wrapper<TransitioningProperty<Value>>> prior;
+ TimePoint begin;
+ TimePoint end;
+ Value value;
+};
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/types.cpp b/src/mbgl/style/types.cpp
index bf2ba8a793..b37e73ffb1 100644
--- a/src/mbgl/style/types.cpp
+++ b/src/mbgl/style/types.cpp
@@ -89,4 +89,9 @@ MBGL_DEFINE_ENUM(IconTextFitType, {
{ IconTextFitType::Height, "height" },
});
+MBGL_DEFINE_ENUM(LightAnchorType, {
+ { LightAnchorType::Map, "map" },
+ { LightAnchorType::Viewport, "viewport" }
+});
+
} // namespace mbgl
diff --git a/src/mbgl/util/mat3.cpp b/src/mbgl/util/mat3.cpp
index e2200867ce..107be81985 100644
--- a/src/mbgl/util/mat3.cpp
+++ b/src/mbgl/util/mat3.cpp
@@ -25,8 +25,9 @@
#include <cmath>
namespace mbgl {
+namespace matrix {
-void matrix::identity(mat3& out) {
+void identity(mat3& out) {
out[0] = 1.0f;
out[1] = 0.0f;
out[2] = 0.0f;
@@ -38,7 +39,7 @@ void matrix::identity(mat3& out) {
out[8] = 1.0f;
}
-void matrix::translate(mat3& out, const mat3& a, double x, double y) {
+void translate(mat3& out, const mat3& a, double x, double y) {
double a00 = a[0], a01 = a[1], a02 = a[2],
a10 = a[3], a11 = a[4], a12 = a[5],
a20 = a[6], a21 = a[7], a22 = a[8];
@@ -56,7 +57,7 @@ void matrix::translate(mat3& out, const mat3& a, double x, double y) {
out[8] = x * a02 + y * a12 + a22;
}
-void matrix::rotate(mat3& out, const mat3& a, double rad) {
+void rotate(mat3& out, const mat3& a, double rad) {
double s = std::sin(rad),
c = std::cos(rad),
a00 = a[0],
@@ -82,7 +83,7 @@ void matrix::rotate(mat3& out, const mat3& a, double rad) {
out[8] = a22;
}
-void matrix::scale(mat3& out, const mat3& a, double x, double y) {
+void scale(mat3& out, const mat3& a, double x, double y) {
out[0] = x * a[0];
out[1] = x * a[1];
out[2] = x * a[2];
@@ -94,4 +95,11 @@ void matrix::scale(mat3& out, const mat3& a, double x, double y) {
out[8] = a[8];
}
+void transformMat3f(vec3f& out, const vec3f& a, const mat3& m) {
+ out[0] = m[0] * a[0] + m[3] * a[1] + m[6] * a[2];
+ out[1] = m[1] * a[0] + m[4] * a[1] + m[7] * a[2];
+ out[2] = m[2] * a[0] + m[5] * a[1] + m[8] * a[2];
+}
+
+} // namespace matrix
} // namespace mbgl
diff --git a/src/mbgl/util/mat3.hpp b/src/mbgl/util/mat3.hpp
index ca4955fe5f..c4203c940b 100644
--- a/src/mbgl/util/mat3.hpp
+++ b/src/mbgl/util/mat3.hpp
@@ -26,7 +26,9 @@
namespace mbgl {
-typedef std::array<double, 9> mat3;
+using vec3 = std::array<double, 3>;
+using vec3f = std::array<float, 3>;
+using mat3 = std::array<double, 9>;
namespace matrix {
@@ -35,5 +37,7 @@ void translate(mat3& out, const mat3& a, double x, double y);
void rotate(mat3& out, const mat3& a, double rad);
void scale(mat3& out, const mat3& a, double x, double y);
+void transformMat3f(vec3f& out, const vec3f& a, const mat3& m);
+
} // namespace matrix
} // namespace mbgl
diff --git a/src/mbgl/util/offscreen_texture.cpp b/src/mbgl/util/offscreen_texture.cpp
index 56ef60b15c..e719ac566e 100644
--- a/src/mbgl/util/offscreen_texture.cpp
+++ b/src/mbgl/util/offscreen_texture.cpp
@@ -14,12 +14,13 @@ public:
void bind() {
if (!framebuffer) {
- texture = context.createTexture(size);
+ texture = context.createTexture(size, gl::TextureFormat::RGBA);
framebuffer = context.createFramebuffer(*texture);
} else {
context.bindFramebuffer = framebuffer->framebuffer;
}
+ context.activeTexture = 0;
context.viewport = { 0, 0, size };
}
@@ -36,6 +37,20 @@ public:
return size;
}
+ void bindRenderbuffers(gl::TextureUnit unit) {
+ if (!framebuffer) {
+ texture = context.createTexture(size, gl::TextureFormat::RGBA, unit);
+ gl::Renderbuffer<gl::RenderbufferType::DepthComponent> depthTarget = context.createRenderbuffer<gl::RenderbufferType::DepthComponent>(size);
+ framebuffer = context.createFramebuffer(*texture, depthTarget);
+
+ } else {
+ context.bindFramebuffer = framebuffer->framebuffer;
+ }
+
+ context.activeTexture = unit;
+ context.viewport = { 0, 0, size };
+ }
+
private:
gl::Context& context;
const Size size;
@@ -66,4 +81,8 @@ const Size& OffscreenTexture::getSize() const {
return impl->getSize();
}
+void OffscreenTexture::bindRenderbuffers(gl::TextureUnit unit) {
+ impl->bindRenderbuffers(unit);
+}
+
} // namespace mbgl
diff --git a/src/mbgl/util/offscreen_texture.hpp b/src/mbgl/util/offscreen_texture.hpp
index b8bfabf7d3..4e9e936114 100644
--- a/src/mbgl/util/offscreen_texture.hpp
+++ b/src/mbgl/util/offscreen_texture.hpp
@@ -16,6 +16,7 @@ public:
~OffscreenTexture();
void bind() override;
+ void bindRenderbuffers(gl::TextureUnit unit = 0);
PremultipliedImage readStillImage();
diff --git a/test/fixtures/style_parser/line-translate.info.json b/test/fixtures/style_parser/line-translate.info.json
index 16df094290..26df77609b 100644
--- a/test/fixtures/style_parser/line-translate.info.json
+++ b/test/fixtures/style_parser/line-translate.info.json
@@ -1,7 +1,7 @@
{
"default": {
"log": [
- [1, "WARNING", "ParseStyle", "value must be an array of two numbers"]
+ [1, "WARNING", "ParseStyle", "value must be an array of 2 numbers"]
]
}
}
diff --git a/test/style/conversion/light.test.cpp b/test/style/conversion/light.test.cpp
new file mode 100644
index 0000000000..da0fd3054a
--- /dev/null
+++ b/test/style/conversion/light.test.cpp
@@ -0,0 +1,101 @@
+#include <mbgl/test/util.hpp>
+
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/rapidjson_conversion.hpp>
+#include <mbgl/style/conversion/constant.hpp>
+#include <mbgl/style/conversion/light.hpp>
+#include <mbgl/style/position.hpp>
+#include <mbgl/util/rapidjson.hpp>
+#include <mbgl/util/color.hpp>
+#include <mbgl/util/chrono.hpp>
+
+#include <array>
+
+using namespace mbgl;
+using namespace mbgl::style;
+using namespace mbgl::style::conversion;
+
+TEST(StyleConversion, Light) {
+ Error error;
+
+ auto parseLight = [&](const std::string& src) {
+ JSDocument doc;
+ doc.Parse<0>(src);
+ return convert<Light>(doc, error);
+ };
+
+ {
+ auto light = parseLight("{}");
+ ASSERT_TRUE((bool) light);
+ }
+
+ {
+ auto light = parseLight("{\"color\":{\"stops\":[[14,\"blue\"],[16,\"red\"]]},\"intensity\":0.3,\"position\":[3,90,90]}");
+ ASSERT_TRUE((bool) light);
+
+ ASSERT_TRUE(light->get<LightAnchor>().value.isUndefined());
+ ASSERT_FALSE(light->get<LightAnchor>().value.isConstant());
+ ASSERT_FALSE(light->get<LightAnchor>().value.isCameraFunction());
+
+ ASSERT_FALSE(light->get<LightIntensity>().value.isUndefined());
+ ASSERT_TRUE(light->get<LightIntensity>().value.isConstant());
+ ASSERT_EQ(light->get<LightIntensity>().value.asConstant(), 0.3f);
+ ASSERT_FALSE(light->get<LightAnchor>().value.isCameraFunction());
+
+ ASSERT_FALSE(light->get<LightColor>().value.isUndefined());
+ ASSERT_FALSE(light->get<LightColor>().value.isConstant());
+ ASSERT_TRUE(light->get<LightColor>().value.isCameraFunction());
+
+ ASSERT_FALSE(light->get<LightPosition>().value.isUndefined());
+ ASSERT_TRUE(light->get<LightPosition>().value.isConstant());
+ std::array<float, 3> expected{{ 3, 90, 90 }};
+ ASSERT_EQ(light->get<LightPosition>().value.asConstant(), mbgl::style::Position({ expected }));
+ ASSERT_FALSE(light->get<LightPosition>().value.isCameraFunction());
+ }
+
+ {
+ auto light = parseLight("{\"color\":\"blue\",\"intensity\":0.3,\"color-transition\":{\"duration\":1000}}");
+ ASSERT_TRUE((bool) light);
+
+ ASSERT_FALSE(light->get<LightColor>().value.isUndefined());
+ ASSERT_TRUE(light->get<LightColor>().value.isConstant());
+ ASSERT_FALSE(light->get<LightColor>().value.isCameraFunction());
+ ASSERT_EQ(light->get<LightColor>().transition.duration, mbgl::Duration(mbgl::Milliseconds(1000)));
+ ASSERT_FALSE((bool) light->get<LightColor>().transition.delay);
+ }
+
+ {
+ auto light = parseLight("{\"intensity\":false}");
+
+ ASSERT_FALSE((bool) light);
+ ASSERT_EQ("value must be a number", error.message);
+ }
+
+ {
+ auto light = parseLight("{\"intensity\":{\"stops\":[[15,\"red\"],[17,\"blue\"]]}}");
+
+ ASSERT_FALSE((bool) light);
+ ASSERT_EQ("value must be a number", error.message);
+ }
+
+ {
+ auto light = parseLight("{\"color\":5}");
+
+ ASSERT_FALSE((bool) light);
+ ASSERT_EQ("value must be a string", error.message);
+ }
+
+ {
+ auto light = parseLight("{\"position\":[0,5]}");
+
+ ASSERT_FALSE((bool) light);
+ ASSERT_EQ("value must be an array of 3 numbers", error.message);
+ }
+
+ {
+ auto light = parseLight("{\"anchor\":\"something\"}");
+
+ ASSERT_FALSE((bool) light);
+ ASSERT_EQ("value must be a valid enumeration value", error.message);
+ }
+}
diff --git a/test/style/paint_property.test.cpp b/test/style/paint_property.test.cpp
index 39d31068c1..15a0796f21 100644
--- a/test/style/paint_property.test.cpp
+++ b/test/style/paint_property.test.cpp
@@ -1,12 +1,13 @@
#include <mbgl/test/util.hpp>
#include <mbgl/style/paint_property.hpp>
+#include <mbgl/style/transitioning_property.hpp>
using namespace mbgl;
using namespace mbgl::style;
using namespace std::literals::chrono_literals;
-float evaluate(UnevaluatedPaintProperty<PropertyValue<float>>& property, Duration delta = Duration::zero()) {
+float evaluate(TransitioningProperty<PropertyValue<float>>& property, Duration delta = Duration::zero()) {
PropertyEvaluationParameters parameters {
0,
TimePoint::min() + delta,
@@ -22,7 +23,7 @@ float evaluate(UnevaluatedPaintProperty<PropertyValue<float>>& property, Duratio
return property.evaluate(evaluator, parameters.now);
}
-PossiblyEvaluatedPropertyValue<float> evaluate(UnevaluatedPaintProperty<DataDrivenPropertyValue<float>>& property, Duration delta = Duration::zero()) {
+PossiblyEvaluatedPropertyValue<float> evaluate(TransitioningProperty<DataDrivenPropertyValue<float>>& property, Duration delta = Duration::zero()) {
PropertyEvaluationParameters parameters {
0,
TimePoint::min() + delta,
@@ -38,15 +39,15 @@ PossiblyEvaluatedPropertyValue<float> evaluate(UnevaluatedPaintProperty<DataDriv
return property.evaluate(evaluator, parameters.now);
}
-TEST(UnevaluatedPaintProperty, EvaluateDefaultValue) {
- UnevaluatedPaintProperty<PropertyValue<float>> property;
+TEST(TransitioningProperty, EvaluateDefaultValue) {
+ TransitioningProperty<PropertyValue<float>> property;
ASSERT_EQ(0.0f, evaluate(property));
}
-TEST(UnevaluatedPaintProperty, EvaluateUntransitionedConstant) {
- UnevaluatedPaintProperty<PropertyValue<float>> property {
+TEST(TransitioningProperty, EvaluateUntransitionedConstant) {
+ TransitioningProperty<PropertyValue<float>> property {
PropertyValue<float>(1.0f),
- UnevaluatedPaintProperty<PropertyValue<float>>(),
+ TransitioningProperty<PropertyValue<float>>(),
TransitionOptions(),
TimePoint::min()
};
@@ -54,18 +55,18 @@ TEST(UnevaluatedPaintProperty, EvaluateUntransitionedConstant) {
ASSERT_EQ(1.0f, evaluate(property));
}
-TEST(UnevaluatedPaintProperty, EvaluateTransitionedConstantWithoutDelay) {
+TEST(TransitioningProperty, EvaluateTransitionedConstantWithoutDelay) {
TransitionOptions transition;
transition.duration = { 1000ms };
- UnevaluatedPaintProperty<PropertyValue<float>> t0 {
+ TransitioningProperty<PropertyValue<float>> t0 {
PropertyValue<float>(0.0f),
- UnevaluatedPaintProperty<PropertyValue<float>>(),
+ TransitioningProperty<PropertyValue<float>>(),
TransitionOptions(),
TimePoint::min()
};
- UnevaluatedPaintProperty<PropertyValue<float>> t1 {
+ TransitioningProperty<PropertyValue<float>> t1 {
PropertyValue<float>(1.0f),
t0,
transition,
@@ -77,19 +78,19 @@ TEST(UnevaluatedPaintProperty, EvaluateTransitionedConstantWithoutDelay) {
ASSERT_FLOAT_EQ(1.0f, evaluate(t1, 1500ms));
}
-TEST(UnevaluatedPaintProperty, EvaluateTransitionedConstantWithDelay) {
+TEST(TransitioningProperty, EvaluateTransitionedConstantWithDelay) {
TransitionOptions transition;
transition.delay = { 1000ms };
transition.duration = { 1000ms };
- UnevaluatedPaintProperty<PropertyValue<float>> t0 {
+ TransitioningProperty<PropertyValue<float>> t0 {
PropertyValue<float>(0.0f),
- UnevaluatedPaintProperty<PropertyValue<float>>(),
+ TransitioningProperty<PropertyValue<float>>(),
TransitionOptions(),
TimePoint::min()
};
- UnevaluatedPaintProperty<PropertyValue<float>> t1 {
+ TransitioningProperty<PropertyValue<float>> t1 {
PropertyValue<float>(1.0f),
t0,
transition,
@@ -103,14 +104,14 @@ TEST(UnevaluatedPaintProperty, EvaluateTransitionedConstantWithDelay) {
ASSERT_FLOAT_EQ(1.0f, evaluate(t1, 2500ms));
}
-TEST(UnevaluatedPaintProperty, EvaluateDataDrivenValue) {
+TEST(TransitioningProperty, EvaluateDataDrivenValue) {
TransitionOptions transition;
transition.delay = { 1000ms };
transition.duration = { 1000ms };
- UnevaluatedPaintProperty<DataDrivenPropertyValue<float>> t0 {
+ TransitioningProperty<DataDrivenPropertyValue<float>> t0 {
DataDrivenPropertyValue<float>(0.0f),
- UnevaluatedPaintProperty<DataDrivenPropertyValue<float>>(),
+ TransitioningProperty<DataDrivenPropertyValue<float>>(),
TransitionOptions(),
TimePoint::min()
};
@@ -120,7 +121,7 @@ TEST(UnevaluatedPaintProperty, EvaluateDataDrivenValue) {
IdentityStops<float>()
};
- UnevaluatedPaintProperty<DataDrivenPropertyValue<float>> t1 {
+ TransitioningProperty<DataDrivenPropertyValue<float>> t1 {
DataDrivenPropertyValue<float>(sourceFunction),
t0,
transition,
diff --git a/test/util/position.test.cpp b/test/util/position.test.cpp
new file mode 100644
index 0000000000..938a08dded
--- /dev/null
+++ b/test/util/position.test.cpp
@@ -0,0 +1,49 @@
+#include <mbgl/test/util.hpp>
+
+#include <mbgl/style/types.hpp>
+#include <mbgl/style/position.hpp>
+#include <mbgl/util/constants.hpp>
+
+using namespace mbgl;
+using namespace style;
+
+void expectArrayEQ(std::array<float, 3> got, std::array<float, 3> expected) {
+ for (int i = 0; i < 3; i++) {
+ EXPECT_NEAR(got[i], expected[i], 0.00001);
+ }
+};
+
+void expectArrayNE(std::array<float, 3> got, std::array<float, 3> expected) {
+ short eq = 0;
+ for (int i = 0; i < 3; i++) {
+ if (got[i] == expected[i]) {
+ eq++;
+ }
+ }
+ EXPECT_NE(eq, 3);
+};
+
+Position createPosition(std::array<float, 3> pos) {
+ return Position(pos);
+}
+
+TEST(Position, Calculations) {
+ std::array<float, 3> spherical{{ 2, 10, 270 }};
+
+ Position position(spherical);
+
+ expectArrayNE(position.getCartesian(), spherical);
+ expectArrayEQ(position.getSpherical(), spherical);
+
+ expectArrayEQ(position.getCartesian(), {{ 0.34729638695716858, -1.9696154594421387, 2.384976127700611e-08 }});
+
+ expectArrayNE(createPosition({{ 2, 30, 10 }}).getSpherical(), createPosition({{ 2, 30, 370 }}).getSpherical());
+ expectArrayEQ(createPosition({{ 2, 30, 10 }}).getCartesian(), createPosition({{ 2, 30, 370 }}).getCartesian());
+
+ std::array<float, 3> newSpherical = {{ 1, 80, 270 }};
+ position.set(newSpherical);
+
+ expectArrayNE(position.getSpherical(), spherical);
+ expectArrayNE(position.getCartesian(), {{ 0.34729638695716858, -1.9696154594421387, 2.384976127700611e-08 }});
+ expectArrayEQ(position.getCartesian(), {{ 0.98480772972106934, -0.17364829778671265, 1.1924880638503055e-08 }});
+}
diff --git a/test/util/tile_cover.test.cpp b/test/util/tile_cover.test.cpp
index 809b7df9f2..c746e6dab5 100644
--- a/test/util/tile_cover.test.cpp
+++ b/test/util/tile_cover.test.cpp
@@ -31,12 +31,13 @@ TEST(TileCover, Pitch) {
Transform transform;
transform.resize({ 512, 512 });
// slightly offset center so that tile order is better defined
- transform.setLatLng({ 0.01, -0.01 });
+ transform.setLatLng({ 0.1, -0.1 });
transform.setZoom(2);
+ transform.setAngle(5.0);
transform.setPitch(40.0 * M_PI / 180.0);
EXPECT_EQ((std::vector<UnwrappedTileID>{
- { 2, 1, 1 }, { 2, 1, 2 }, { 2, 2, 1 }, { 2, 2, 2 },
+ { 2, 1, 2 }, { 2, 1, 1 }, { 2, 2, 2 }, { 2, 2, 1 }, { 2, 3, 2 }
}),
util::tileCover(transform.getState(), 2));
}