Add run-time plug-in layer support to iOS (#3430)
This commit is contained in:
@@ -4,6 +4,8 @@ load("@npm//:defs.bzl", "npm_link_all_packages")
|
||||
load("@rules_cc//cc:defs.bzl", "cc_library")
|
||||
load(
|
||||
"//bazel:core.bzl",
|
||||
"MLN_LAYER_PLUGIN_HEADERS",
|
||||
"MLN_LAYER_PLUGIN_SOURCE",
|
||||
"MLN_CORE_HEADERS",
|
||||
"MLN_CORE_SOURCE",
|
||||
"MLN_DRAWABLES_GL_HEADERS",
|
||||
@@ -76,14 +78,15 @@ cc_library(
|
||||
# the prefix from the path.
|
||||
cc_library(
|
||||
name = "mbgl-core",
|
||||
srcs = MLN_CORE_SOURCE +
|
||||
srcs = MLN_LAYER_PLUGIN_SOURCE +
|
||||
MLN_CORE_SOURCE +
|
||||
MLN_GENERATED_SHADER_SOURCE +
|
||||
MLN_GENERATED_STYLE_SOURCE + select({
|
||||
":drawable_renderer": MLN_OPENGL_SOURCE + MLN_DRAWABLES_SOURCE + MLN_DRAWABLES_GL_SOURCE,
|
||||
":metal_renderer": MLN_DRAWABLES_SOURCE + MLN_DRAWABLES_MTL_SOURCE,
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
hdrs = MLN_CORE_HEADERS + select({
|
||||
hdrs = MLN_LAYER_PLUGIN_HEADERS + MLN_CORE_HEADERS + select({
|
||||
":drawable_renderer": MLN_OPENGL_HEADERS + MLN_DRAWABLES_HEADERS + MLN_DRAWABLES_GL_HEADERS,
|
||||
":metal_renderer": MLN_DRAWABLES_HEADERS + MLN_DRAWABLES_MTL_HEADERS,
|
||||
"//conditions:default": [],
|
||||
|
||||
@@ -966,6 +966,19 @@ list(APPEND SRC_FILES
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/util/version.hpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/util/work_request.cpp
|
||||
)
|
||||
list(APPEND SRC_FILES
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/plugin/plugin_layer.hpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/plugin/plugin_layer_render.hpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/plugin/plugin_layer_properties.hpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/plugin/plugin_layer_impl.hpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/plugin/plugin_layer_factory.hpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/plugin/plugin_layer.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/plugin/plugin_layer_render.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/plugin/plugin_layer_properties.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/plugin/plugin_layer_impl.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/plugin/plugin_layer_factory.cpp
|
||||
)
|
||||
|
||||
|
||||
if(MLN_WITH_OPENGL)
|
||||
message(STATUS "Configuring GL-Native with OpenGL renderer backend")
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
MLN_LAYER_PLUGIN_HEADERS = [
|
||||
"src/mbgl/plugin/plugin_layer.hpp",
|
||||
"src/mbgl/plugin/plugin_layer_factory.hpp",
|
||||
"src/mbgl/plugin/plugin_layer_impl.hpp",
|
||||
"src/mbgl/plugin/plugin_layer_render.hpp",
|
||||
"src/mbgl/plugin/plugin_layer_properties.hpp",
|
||||
]
|
||||
|
||||
MLN_LAYER_PLUGIN_SOURCE = [
|
||||
"src/mbgl/plugin/plugin_layer.cpp",
|
||||
"src/mbgl/plugin/plugin_layer_factory.cpp",
|
||||
"src/mbgl/plugin/plugin_layer_impl.cpp",
|
||||
"src/mbgl/plugin/plugin_layer_render.cpp",
|
||||
"src/mbgl/plugin/plugin_layer_properties.cpp",
|
||||
]
|
||||
|
||||
MLN_PUBLIC_GENERATED_STYLE_HEADERS = [
|
||||
"include/mbgl/style/layers/circle_layer.hpp",
|
||||
"include/mbgl/style/layers/fill_layer.hpp",
|
||||
|
||||
@@ -67,6 +67,15 @@ public:
|
||||
*/
|
||||
static const bool annotationsEnabled;
|
||||
|
||||
/**
|
||||
* Enables a layer type for JSON style only.
|
||||
*
|
||||
* We might not want to expose runtime API for some layer types
|
||||
* in order to save binary size (the corresponding SDK layer wrappers
|
||||
* should be excluded from the project build).
|
||||
*/
|
||||
virtual void addLayerTypeCoreOnly(std::unique_ptr<mbgl::LayerFactory>);
|
||||
|
||||
protected:
|
||||
virtual ~LayerManager() = default;
|
||||
virtual LayerFactory* getFactory(const std::string& type) noexcept = 0;
|
||||
|
||||
@@ -90,6 +90,10 @@ struct LayerTypeInfo {
|
||||
} tileKind;
|
||||
};
|
||||
|
||||
// Added this to support plugins and that their LayerTypeInfo isn't the same point
|
||||
// across the board
|
||||
bool layerTypeInfoEquals(const mbgl::style::LayerTypeInfo* one, const mbgl::style::LayerTypeInfo* other);
|
||||
|
||||
/**
|
||||
* The runtime representation of a [layer](https://maplibre.org/maplibre-style-spec/#layers)
|
||||
* from the MapLibre Style Spec.
|
||||
|
||||
@@ -266,6 +266,10 @@ objc_library(
|
||||
objc_library(
|
||||
name = "iosapp",
|
||||
srcs = [
|
||||
"//platform/darwin:app/PluginLayerExample.h",
|
||||
"//platform/darwin:app/PluginLayerExample.mm",
|
||||
"//platform/darwin:app/PluginLayerExampleMetalRendering.h",
|
||||
"//platform/darwin:app/PluginLayerExampleMetalRendering.mm",
|
||||
"//platform/darwin:app/CustomStyleLayerExample.h",
|
||||
"//platform/darwin:app/CustomStyleLayerExample.m",
|
||||
"//platform/ios:ios_app_srcs",
|
||||
|
||||
@@ -174,6 +174,7 @@ swift_library(
|
||||
data = [
|
||||
"test/amsterdam.geojson",
|
||||
"test/one-liner.json",
|
||||
"app/PluginLayerTestStyle.json",
|
||||
],
|
||||
visibility = [
|
||||
"//platform/ios/test:__pkg__",
|
||||
@@ -297,6 +298,11 @@ objc_library(
|
||||
|
||||
exports_files(
|
||||
[
|
||||
"app/PluginLayerTestStyle.json",
|
||||
"app/PluginLayerExample.h",
|
||||
"app/PluginLayerExample.mm",
|
||||
"app/PluginLayerExampleMetalRendering.h",
|
||||
"app/PluginLayerExampleMetalRendering.mm",
|
||||
"test/amsterdam.geojson",
|
||||
"test/MLNSDKTestHelpers.swift",
|
||||
"app/CustomStyleLayerExample.h",
|
||||
|
||||
9
platform/darwin/app/PluginLayerExample.h
Normal file
9
platform/darwin/app/PluginLayerExample.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "MLNPluginLayer.h"
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface PluginLayerExample : MLNPluginLayer
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
30
platform/darwin/app/PluginLayerExample.mm
Normal file
30
platform/darwin/app/PluginLayerExample.mm
Normal file
@@ -0,0 +1,30 @@
|
||||
#import "PluginLayerExample.h"
|
||||
|
||||
@implementation PluginLayerExample
|
||||
|
||||
|
||||
// This is the layer type in the style that is used
|
||||
+(MLNPluginLayerCapabilities *)layerCapabilities {
|
||||
|
||||
MLNPluginLayerCapabilities *tempResult = [[MLNPluginLayerCapabilities alloc] init];
|
||||
tempResult.layerID = @"plugin-layer-test";
|
||||
tempResult.requiresPass3D = YES;
|
||||
return tempResult;
|
||||
|
||||
}
|
||||
|
||||
// The overrides
|
||||
-(void)onRenderLayer {
|
||||
NSLog(@"PluginLayerExample: On Render Layer");
|
||||
|
||||
}
|
||||
|
||||
-(void)onUpdateLayer {
|
||||
|
||||
}
|
||||
|
||||
-(void)onUpdateLayerProperties:(NSDictionary *)layerProperties {
|
||||
// NSLog(@"Layer Properties: %@", layerProperties);
|
||||
}
|
||||
|
||||
@end
|
||||
17
platform/darwin/app/PluginLayerExampleMetalRendering.h
Normal file
17
platform/darwin/app/PluginLayerExampleMetalRendering.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
PluginLayerExampleMetalRendering
|
||||
|
||||
This is a port lf the CustomStyleLayerExample into the plug-in architecture. It assumes
|
||||
that the rendering is being done in metal since it's added to the core via a darwin method
|
||||
|
||||
*/
|
||||
|
||||
#import "MLNPluginLayer.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface PluginLayerExampleMetalRendering : MLNPluginLayer
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
233
platform/darwin/app/PluginLayerExampleMetalRendering.mm
Normal file
233
platform/darwin/app/PluginLayerExampleMetalRendering.mm
Normal file
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
PluginLayerExampleMetalRendering
|
||||
|
||||
This is a port lf the CustomStyleLayerExample into the plug-in architecture. It assumes
|
||||
that the rendering is being done in metal since it's added to the core via a darwin method
|
||||
|
||||
*/
|
||||
|
||||
#import "PluginLayerExampleMetalRendering.h"
|
||||
#import <MetalKit/MetalKit.h>
|
||||
#import "Mapbox.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
vector_float2 position;
|
||||
vector_float4 color;
|
||||
} Vertex;
|
||||
|
||||
@interface PluginLayerExampleMetalRendering () {
|
||||
// The render pipeline state
|
||||
id<MTLRenderPipelineState> _pipelineState;
|
||||
id<MTLDepthStencilState> _depthStencilStateWithoutStencil;
|
||||
|
||||
// Properties
|
||||
float _offsetX;
|
||||
float _offsetY;
|
||||
float _scale;
|
||||
float _r, _g, _b, _a;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation PluginLayerExampleMetalRendering
|
||||
|
||||
|
||||
// This is a static class method that is used by the MLNMapView to create the
|
||||
// required marshalling classes to interface with the MapLibre core.
|
||||
+(MLNPluginLayerCapabilities *)layerCapabilities {
|
||||
|
||||
MLNPluginLayerCapabilities *tempResult = [[MLNPluginLayerCapabilities alloc] init];
|
||||
tempResult.layerID = @"plugin-layer-metal-rendering";
|
||||
tempResult.requiresPass3D = YES;
|
||||
|
||||
// Define the paint properties that this layer implements and
|
||||
// what types they are
|
||||
tempResult.layerProperties = @[
|
||||
// The scale property
|
||||
[MLNPluginLayerProperty propertyWithName:@"scale"
|
||||
propertyType:MLNPluginLayerPropertyTypeSingleFloat
|
||||
defaultValue:@(1.0)],
|
||||
|
||||
// The fill color property
|
||||
[MLNPluginLayerProperty propertyWithName:@"fill-color"
|
||||
propertyType:MLNPluginLayerPropertyTypeColor
|
||||
defaultValue:[UIColor blueColor]]
|
||||
|
||||
];
|
||||
|
||||
return tempResult;
|
||||
|
||||
}
|
||||
|
||||
- (void)createShaders:(MLNMapView *)mapView {
|
||||
MLNBackendResource* resource = [mapView backendResource];
|
||||
|
||||
NSString *shaderSource = @
|
||||
" #include <metal_stdlib>\n"
|
||||
" using namespace metal;\n"
|
||||
" typedef struct\n"
|
||||
" {\n"
|
||||
" vector_float2 position;\n"
|
||||
" vector_float4 color;\n"
|
||||
" } Vertex;\n"
|
||||
" struct RasterizerData\n"
|
||||
" {\n"
|
||||
" float4 position [[position]];\n"
|
||||
" float4 color;\n"
|
||||
" };\n"
|
||||
" vertex RasterizerData\n"
|
||||
" vertexShader(uint vertexID [[vertex_id]],\n"
|
||||
" constant Vertex *vertices [[buffer(0)]],\n"
|
||||
" constant vector_uint2 *viewportSizePointer [[buffer(1)]])\n"
|
||||
" {\n"
|
||||
" RasterizerData out;\n"
|
||||
" float2 pixelSpacePosition = vertices[vertexID].position.xy;\n"
|
||||
" vector_float2 viewportSize = vector_float2(*viewportSizePointer);\n"
|
||||
" out.position = vector_float4(0.0, 0.0, 0.0, 1.0);\n"
|
||||
" out.position.xy = pixelSpacePosition / (viewportSize / 2.0);\n"
|
||||
" out.color = vertices[vertexID].color;\n"
|
||||
" return out;\n"
|
||||
" }\n"
|
||||
" fragment float4 fragmentShader(RasterizerData in [[stage_in]])\n"
|
||||
" {\n"
|
||||
" return in.color;\n"
|
||||
" }\n";
|
||||
|
||||
|
||||
NSError *error = nil;
|
||||
id<MTLDevice> _device = resource.device;
|
||||
id<MTLLibrary> library = [_device newLibraryWithSource:shaderSource options:nil error:&error];
|
||||
NSAssert(library, @"Error compiling shaders: %@", error);
|
||||
id<MTLFunction> vertexFunction = [library newFunctionWithName:@"vertexShader"];
|
||||
id<MTLFunction> fragmentFunction = [library newFunctionWithName:@"fragmentShader"];
|
||||
|
||||
// Configure a pipeline descriptor that is used to create a pipeline state.
|
||||
MTLRenderPipelineDescriptor *pipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
|
||||
pipelineStateDescriptor.label = @"Simple Pipeline";
|
||||
pipelineStateDescriptor.vertexFunction = vertexFunction;
|
||||
pipelineStateDescriptor.fragmentFunction = fragmentFunction;
|
||||
pipelineStateDescriptor.colorAttachments[0].pixelFormat = resource.mtkView.colorPixelFormat;
|
||||
pipelineStateDescriptor.depthAttachmentPixelFormat = MTLPixelFormatDepth32Float_Stencil8;
|
||||
pipelineStateDescriptor.stencilAttachmentPixelFormat = MTLPixelFormatDepth32Float_Stencil8;
|
||||
|
||||
_pipelineState = [_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor
|
||||
error:&error];
|
||||
NSAssert(_pipelineState, @"Failed to create pipeline state: %@", error);
|
||||
|
||||
// Notice that we don't configure the stencilTest property, leaving stencil testing disabled
|
||||
MTLDepthStencilDescriptor *depthStencilDescriptor = [[MTLDepthStencilDescriptor alloc] init];
|
||||
depthStencilDescriptor.depthCompareFunction = MTLCompareFunctionAlways; // Or another value as needed
|
||||
depthStencilDescriptor.depthWriteEnabled = NO;
|
||||
|
||||
_depthStencilStateWithoutStencil = [_device newDepthStencilStateWithDescriptor:depthStencilDescriptor];
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Setup the rendering environment
|
||||
-(void)setupRendering:(MLNMapView *)mapView {
|
||||
[self createShaders:mapView];
|
||||
if (_scale == 0) {
|
||||
_scale = 1;
|
||||
}
|
||||
// Set the initial
|
||||
_a = 1;
|
||||
|
||||
}
|
||||
|
||||
// The overrides
|
||||
-(void)onRenderLayer:(MLNMapView *)mapView
|
||||
renderEncoder:(id<MTLRenderCommandEncoder>)renderEncoder {
|
||||
|
||||
MLNBackendResource* resource = [mapView backendResource];
|
||||
|
||||
if (_pipelineState == nil) {
|
||||
[self setupRendering:mapView];
|
||||
}
|
||||
|
||||
if (renderEncoder == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
vector_uint2 _viewportSize;
|
||||
_viewportSize.x = resource.mtkView.drawableSize.width;
|
||||
_viewportSize.y = resource.mtkView.drawableSize.height;
|
||||
|
||||
Vertex triangleVerticesWithColor[] = {
|
||||
// 2D positions, RGBA colors
|
||||
{ { (250 + _offsetX) * _scale, (-250 + _offsetY) * _scale }, { 1, 1, 1, _a } },
|
||||
{ { (-250 + _offsetX) * _scale, (-250 + _offsetY) * _scale }, { 1, 1, 1, _a} },
|
||||
{ { (0 + _offsetX) * _scale, (250 + _offsetY) * _scale }, { _r, _g, _b, _a } },
|
||||
};
|
||||
|
||||
[renderEncoder setRenderPipelineState:_pipelineState];
|
||||
[renderEncoder setDepthStencilState:_depthStencilStateWithoutStencil];
|
||||
|
||||
// Pass in the parameter data.
|
||||
[renderEncoder setVertexBytes:triangleVerticesWithColor
|
||||
length:sizeof(triangleVerticesWithColor)
|
||||
atIndex:0];
|
||||
|
||||
[renderEncoder setVertexBytes:&_viewportSize
|
||||
length:sizeof(_viewportSize)
|
||||
atIndex:1];
|
||||
|
||||
// Draw the triangle.
|
||||
[renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle
|
||||
vertexStart:0
|
||||
vertexCount:3];
|
||||
}
|
||||
|
||||
-(void)onUpdateLayer:(MLNPluginLayerDrawingContext)drawingContext {
|
||||
// This is called before the render call and is a place where
|
||||
// any animations/etc can be updated
|
||||
}
|
||||
|
||||
-(void)onUpdateLayerProperties:(NSDictionary *)layerProperties {
|
||||
//NSLog(@"Metal Layer Rendering Properties: %@", layerProperties);
|
||||
|
||||
// These two properties are in the "properties" section of the style sheet and
|
||||
// are passed in at layer creation. These are not expression based properties
|
||||
NSNumber *offsetX = [layerProperties objectForKey:@"offset-x"];
|
||||
if (offsetX) {
|
||||
_offsetX = [[layerProperties objectForKey:@"offset-x"] floatValue];
|
||||
}
|
||||
|
||||
NSNumber *offsetY = [layerProperties objectForKey:@"offset-y"];
|
||||
if (offsetY) {
|
||||
_offsetY = [[layerProperties objectForKey:@"offset-y"] floatValue];
|
||||
}
|
||||
|
||||
// These are the paint properties and can be updated each frame or zoom level
|
||||
// change/etc depending on if they are static or expression based/etc.
|
||||
NSNumber *scale = [layerProperties objectForKey:@"scale"];
|
||||
if (scale) {
|
||||
if ([scale isKindOfClass:[NSNumber class]]) {
|
||||
_scale = [scale floatValue];
|
||||
}
|
||||
}
|
||||
|
||||
NSString *fillColor = [layerProperties objectForKey:@"fill-color"];
|
||||
if (fillColor) {
|
||||
//NSLog(@"Fill Color: %@", fillColor);
|
||||
fillColor = [fillColor stringByReplacingOccurrencesOfString:@"rgba(" withString:@""];
|
||||
fillColor = [fillColor stringByReplacingOccurrencesOfString:@")" withString:@""];
|
||||
NSArray *components = [fillColor componentsSeparatedByString:@","];
|
||||
if ([components count] == 4) {
|
||||
// Convert to float since they come in as 0..255
|
||||
_r = [[components objectAtIndex:0] floatValue] / 255.0;
|
||||
_g = [[components objectAtIndex:1] floatValue] / 255.0;
|
||||
_b = [[components objectAtIndex:2] floatValue] / 255.0;
|
||||
_a = [[components objectAtIndex:3] floatValue];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
2124
platform/darwin/app/PluginLayerTestStyle.json
Normal file
2124
platform/darwin/app/PluginLayerTestStyle.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -106,6 +106,8 @@ MLN_DARWIN_OBJC_HEADERS = [
|
||||
"src/NSExpression+MLNAdditions.h",
|
||||
"src/NSPredicate+MLNAdditions.h",
|
||||
"src/NSValue+MLNAdditions.h",
|
||||
"src/MLNPluginLayer.h",
|
||||
"src/MLNPluginStyleLayer.h",
|
||||
]
|
||||
|
||||
MLN_DARWIN_OBJCPP_HEADERS = [
|
||||
@@ -163,6 +165,8 @@ MLN_DARWIN_PRIVATE_HEADERS = [
|
||||
"src/MLNVectorTileSource_Private.h",
|
||||
"src/NSExpression+MLNPrivateAdditions.h",
|
||||
"src/NSPredicate+MLNPrivateAdditions.h",
|
||||
"src/MLNPluginStyleLayer_Private.h"
|
||||
|
||||
]
|
||||
|
||||
MLN_DARWIN_PUBLIC_OBJCPP_SOURCE = [
|
||||
@@ -217,6 +221,9 @@ MLN_DARWIN_PUBLIC_OBJCPP_SOURCE = [
|
||||
"src/NSExpression+MLNAdditions.mm",
|
||||
"src/NSPredicate+MLNAdditions.mm",
|
||||
"src/NSValue+MLNStyleAttributeAdditions.mm",
|
||||
"src/MLNPluginLayer.mm",
|
||||
"src/MLNPluginStyleLayer.mm"
|
||||
|
||||
]
|
||||
MLN_DARWIN_PUBLIC_OBJCPP_CUSTOM_DRAWABLE_SOURCE = [
|
||||
"src/MLNCustomDrawableStyleLayer_Private.h",
|
||||
|
||||
@@ -162,6 +162,8 @@ add_library(
|
||||
EXCLUDE_FROM_ALL
|
||||
"${CMAKE_CURRENT_LIST_DIR}/app/ExampleCustomDrawableStyleLayer.mm"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/app/CustomStyleLayerExample.m"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/app/PluginLayerExample.mm"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/app/PluginLayerExampleMetalRendering.mm"
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
|
||||
@@ -32,6 +32,11 @@ MLN_EXPORT
|
||||
*/
|
||||
@property (nonatomic, nonnull) MLNActionJournalOptions *actionJournalOptions;
|
||||
|
||||
/**
|
||||
List of plugin classes
|
||||
*/
|
||||
@property NSArray *pluginLayers;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
{
|
||||
_styleURL = nil;
|
||||
_styleJSON = nil;
|
||||
_pluginLayers = nil;
|
||||
|
||||
_actionJournalOptions = [[MLNActionJournalOptions alloc] init];
|
||||
}
|
||||
|
||||
109
platform/darwin/src/MLNPluginLayer.h
Normal file
109
platform/darwin/src/MLNPluginLayer.h
Normal file
@@ -0,0 +1,109 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Metal/Metal.h>
|
||||
#if TARGET_OS_IPHONE
|
||||
#import <UIKit/UIKit.h>
|
||||
#else
|
||||
#import <AppKit/AppKit.h>
|
||||
#endif
|
||||
|
||||
#import "MLNFoundation.h"
|
||||
#import "MLNGeometry.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef enum {
|
||||
MLNPluginLayerPropertyTypeUnknown,
|
||||
MLNPluginLayerPropertyTypeSingleFloat,
|
||||
MLNPluginLayerPropertyTypeColor
|
||||
} MLNPluginLayerPropertyType;
|
||||
|
||||
MLN_EXPORT
|
||||
@interface MLNPluginLayerProperty : NSObject
|
||||
|
||||
+ (MLNPluginLayerProperty *)propertyWithName:(NSString *)propertyName
|
||||
propertyType:(MLNPluginLayerPropertyType)propertyType
|
||||
defaultValue:(id)defaultValue;
|
||||
|
||||
// The name of the property
|
||||
@property (copy) NSString *propertyName;
|
||||
|
||||
// The type of property
|
||||
@property MLNPluginLayerPropertyType propertyType;
|
||||
|
||||
// Single float default value
|
||||
@property float singleFloatDefaultValue;
|
||||
|
||||
// Color default value
|
||||
#if TARGET_OS_IPHONE
|
||||
@property (copy) UIColor *colorDefaultValue;
|
||||
#else
|
||||
@property NSColor *colorDefaultValue;
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
typedef enum {
|
||||
MLNPluginLayerTileKindGeometry,
|
||||
MLNPluginLayerTileKindRaster,
|
||||
MLNPluginLayerTileKindRasterDEM,
|
||||
MLNPluginLayerTileKindNotRequired
|
||||
} MLNPluginLayerTileKind;
|
||||
|
||||
MLN_EXPORT
|
||||
@interface MLNPluginLayerCapabilities : NSObject
|
||||
|
||||
@property (copy) NSString *layerID;
|
||||
@property BOOL requiresPass3D;
|
||||
|
||||
//! This is a list of layer properties that this layer supports.
|
||||
@property (copy) NSArray<MLNPluginLayerProperty *> *layerProperties;
|
||||
|
||||
@end
|
||||
|
||||
@class MLNMapView;
|
||||
|
||||
// Copied initially from MLNStyleLayerDrawingContext. Decided to copy instead of use since we
|
||||
// might add additional properties here
|
||||
/// A structure containing context needed to draw a frame in an ``MLNCustomStyleLayer``.
|
||||
typedef struct MLNPluginLayerDrawingContext {
|
||||
/// The size of the drawable area, in points.
|
||||
CGSize size;
|
||||
/// The center coordinate of the map view.
|
||||
CLLocationCoordinate2D centerCoordinate;
|
||||
/// The current zoom level of the map view.
|
||||
double zoomLevel;
|
||||
/// The heading (direction) in degrees clockwise from true north.
|
||||
CLLocationDirection direction;
|
||||
/// The current pitch of the map view in degrees, measured from the map plane.
|
||||
CGFloat pitch;
|
||||
/// The vertical field of view, in degrees, for the map’s perspective.
|
||||
CGFloat fieldOfView;
|
||||
/// A 4×4 matrix representing the map view’s current projection state.
|
||||
MLNMatrix4 projectionMatrix;
|
||||
} MLNPluginLayerDrawingContext;
|
||||
|
||||
MLN_EXPORT
|
||||
@interface MLNPluginLayer : NSObject
|
||||
|
||||
/// Returns the layer capabilities of the plugin layer.
|
||||
/// This must be overridden by the plug-in layer and return a set of capabilities
|
||||
+ (MLNPluginLayerCapabilities *)layerCapabilities;
|
||||
|
||||
// These are public methods that can be overridden by the plugin layer
|
||||
/// Called when the layer is rendered
|
||||
- (void)onRenderLayer:(MLNMapView *)mapView
|
||||
renderEncoder:(id<MTLRenderCommandEncoder>)renderEncoder;
|
||||
|
||||
/// Called when the layer is updated in the render loop. This would update animations/etc
|
||||
- (void)onUpdateLayer:(MLNPluginLayerDrawingContext)drawingContext;
|
||||
|
||||
/// Called when the layer properties are updated. Can be on initial load from the JSON or when
|
||||
/// dynamic properties are updated
|
||||
- (void)onUpdateLayerProperties:(NSDictionary *)layerProperties;
|
||||
|
||||
/// Added to a map view
|
||||
- (void)didMoveToMapView:(MLNMapView *)mapView;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
80
platform/darwin/src/MLNPluginLayer.mm
Normal file
80
platform/darwin/src/MLNPluginLayer.mm
Normal file
@@ -0,0 +1,80 @@
|
||||
#import "MLNPluginLayer.h"
|
||||
|
||||
@implementation MLNPluginLayerProperty
|
||||
|
||||
+(MLNPluginLayerProperty *)propertyWithName:(NSString *)propertyName
|
||||
propertyType:(MLNPluginLayerPropertyType)propertyType
|
||||
defaultValue:(id)defaultValue {
|
||||
MLNPluginLayerProperty *tempResult = [[MLNPluginLayerProperty alloc] init];
|
||||
tempResult.propertyName = propertyName;
|
||||
tempResult.propertyType = propertyType;
|
||||
|
||||
if (propertyType == MLNPluginLayerPropertyTypeSingleFloat) {
|
||||
if ([defaultValue isKindOfClass:[NSNumber class]]) {
|
||||
tempResult.singleFloatDefaultValue = [defaultValue floatValue];
|
||||
}
|
||||
} else if (propertyType == MLNPluginLayerPropertyTypeColor) {
|
||||
#if TARGET_OS_IPHONE
|
||||
if ([defaultValue isKindOfClass:[UIColor class]]) {
|
||||
#else
|
||||
if ([defaultValue isKindOfClass:[NSColor class]]) {
|
||||
#endif
|
||||
tempResult.colorDefaultValue = defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
return tempResult;
|
||||
}
|
||||
|
||||
|
||||
-(id)init {
|
||||
// Base class implemenation
|
||||
if (self = [super init]) {
|
||||
// Default setup
|
||||
self.propertyType = MLNPluginLayerPropertyTypeUnknown;
|
||||
self.propertyName = @"unknown";
|
||||
|
||||
// Default values for the various types
|
||||
self.singleFloatDefaultValue = 1.0;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MLNPluginLayer
|
||||
|
||||
// This is the layer type in the style that is used
|
||||
+(MLNPluginLayerCapabilities *)layerCapabilities {
|
||||
|
||||
// Base class returns the class name just to return something
|
||||
// TODO: Add an assert/etc or something to notify the developer that this needs to be overridden
|
||||
return nil;
|
||||
|
||||
}
|
||||
|
||||
- (void)onRenderLayer:(MLNMapView *)mapView
|
||||
renderEncoder:(id<MTLRenderCommandEncoder>)renderEncoder {
|
||||
// Base class does nothing
|
||||
}
|
||||
|
||||
- (void)onUpdateLayer:(MLNPluginLayerDrawingContext)drawingContext {
|
||||
// Base class does nothing
|
||||
}
|
||||
|
||||
-(void)onUpdateLayerProperties:(NSDictionary *)layerProperties {
|
||||
// Base class does nothing
|
||||
}
|
||||
|
||||
/// Added to a map view
|
||||
- (void)didMoveToMapView:(MLNMapView *)mapView {
|
||||
// Base class does nothing
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@implementation MLNPluginLayerCapabilities
|
||||
@end
|
||||
13
platform/darwin/src/MLNPluginStyleLayer.h
Normal file
13
platform/darwin/src/MLNPluginStyleLayer.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#import "MLNStyleLayer.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class MLNPluginLayer;
|
||||
|
||||
@interface MLNPluginStyleLayer : MLNStyleLayer
|
||||
|
||||
- (MLNPluginLayer *)pluginLayer;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
35
platform/darwin/src/MLNPluginStyleLayer.mm
Normal file
35
platform/darwin/src/MLNPluginStyleLayer.mm
Normal file
@@ -0,0 +1,35 @@
|
||||
#import "MLNPluginStyleLayer.h"
|
||||
#import "MLNPluginStyleLayer_Private.h"
|
||||
#import <mbgl/plugin/plugin_layer.hpp>
|
||||
#import <mbgl/plugin/plugin_layer_impl.hpp>
|
||||
#import "MLNPluginLayer.h"
|
||||
|
||||
@implementation MLNPluginStyleLayer
|
||||
|
||||
-(void)getStats {
|
||||
|
||||
mbgl::style::PluginLayer *l = (mbgl::style::PluginLayer *)self.rawLayer;
|
||||
auto pl = l->impl();
|
||||
|
||||
}
|
||||
|
||||
-(MLNPluginLayer *)pluginLayer {
|
||||
|
||||
mbgl::style::PluginLayer *l = (mbgl::style::PluginLayer *)self.rawLayer;
|
||||
if (l->_platformReference) {
|
||||
MLNPluginLayer *pl = (__bridge MLNPluginLayer *)l->_platformReference;
|
||||
return pl;
|
||||
}
|
||||
|
||||
return nil;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
|
||||
MLNStyleLayer* mbgl::PluginLayerPeerFactory::createPeer(style::Layer *rawLayer) {
|
||||
return [[MLNPluginStyleLayer alloc] initWithRawLayer:rawLayer];
|
||||
|
||||
}
|
||||
25
platform/darwin/src/MLNPluginStyleLayer_Private.h
Normal file
25
platform/darwin/src/MLNPluginStyleLayer_Private.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "MLNStyleLayer_Private.h"
|
||||
|
||||
#include <mbgl/plugin/plugin_layer_factory.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class PluginLayerPeerFactory : public LayerPeerFactory, public mbgl::PluginLayerFactory {
|
||||
public:
|
||||
PluginLayerPeerFactory(std::string& layerType,
|
||||
mbgl::style::LayerTypeInfo::Source source,
|
||||
mbgl::style::LayerTypeInfo::Pass3D pass3D,
|
||||
mbgl::style::LayerTypeInfo::Layout layout,
|
||||
mbgl::style::LayerTypeInfo::FadingTiles fadingTiles,
|
||||
mbgl::style::LayerTypeInfo::CrossTileIndex crossTileIndex,
|
||||
mbgl::style::LayerTypeInfo::TileKind tileKind)
|
||||
: mbgl::PluginLayerFactory(layerType, source, pass3D, layout, fadingTiles, crossTileIndex, tileKind) {}
|
||||
|
||||
// LayerPeerFactory overrides.
|
||||
LayerFactory* getCoreLayerFactory() final { return this; }
|
||||
virtual MLNStyleLayer* createPeer(style::Layer*) final;
|
||||
};
|
||||
|
||||
} // namespace mbgl
|
||||
@@ -18,12 +18,7 @@ public:
|
||||
|
||||
MLNStyleLayer* createPeer(style::Layer*);
|
||||
|
||||
private:
|
||||
LayerManagerDarwin();
|
||||
/**
|
||||
* Enables a layer type for both JSON style and runtime API.
|
||||
*/
|
||||
void addLayerType(std::unique_ptr<LayerPeerFactory>);
|
||||
public:
|
||||
/**
|
||||
* Enables a layer type for JSON style only.
|
||||
*
|
||||
@@ -31,7 +26,15 @@ private:
|
||||
* in order to save binary size (the corresponding SDK layer wrappers
|
||||
* should be excluded from the project build).
|
||||
*/
|
||||
void addLayerTypeCoreOnly(std::unique_ptr<mbgl::LayerFactory>);
|
||||
void addLayerTypeCoreOnly(std::unique_ptr<mbgl::LayerFactory>) override;
|
||||
|
||||
/**
|
||||
* Enables a layer type for both JSON style and runtime API.
|
||||
*/
|
||||
void addLayerType(std::unique_ptr<LayerPeerFactory>);
|
||||
|
||||
private:
|
||||
LayerManagerDarwin();
|
||||
|
||||
void registerCoreFactory(LayerFactory*);
|
||||
LayerPeerFactory* getPeerFactory(const style::LayerTypeInfo* typeInfo);
|
||||
|
||||
@@ -109,7 +109,7 @@ void LayerManagerDarwin::registerCoreFactory(LayerFactory* factory) {
|
||||
|
||||
LayerPeerFactory* LayerManagerDarwin::getPeerFactory(const mbgl::style::LayerTypeInfo* typeInfo) {
|
||||
for (const auto& factory: peerFactories) {
|
||||
if (factory->getCoreLayerFactory()->getTypeInfo() == typeInfo) {
|
||||
if (layerTypeInfoEquals(factory->getCoreLayerFactory()->getTypeInfo(), typeInfo)) {
|
||||
return factory.get();
|
||||
}
|
||||
}
|
||||
@@ -127,7 +127,8 @@ LayerFactory* LayerManagerDarwin::getFactory(const mbgl::style::LayerTypeInfo* i
|
||||
}
|
||||
|
||||
for (const auto& factory: coreFactories) {
|
||||
if (factory->getTypeInfo() == info) {
|
||||
if (layerTypeInfoEquals(factory->getTypeInfo(), info)) {
|
||||
// if (factory->getTypeInfo() == info) {
|
||||
return factory.get();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,15 @@ class LayerManagerDefault final : public LayerManager {
|
||||
public:
|
||||
LayerManagerDefault();
|
||||
|
||||
/**
|
||||
* Enables a layer type for JSON style only.
|
||||
*
|
||||
* We might not want to expose runtime API for some layer types
|
||||
* in order to save binary size (the corresponding SDK layer wrappers
|
||||
* should be excluded from the project build).
|
||||
*/
|
||||
void addLayerTypeCoreOnly(std::unique_ptr<mbgl::LayerFactory>) override;
|
||||
|
||||
private:
|
||||
void addLayerType(std::unique_ptr<LayerFactory>);
|
||||
// LayerManager overrides.
|
||||
@@ -34,6 +43,10 @@ private:
|
||||
std::map<std::string, LayerFactory*> typeToFactory;
|
||||
};
|
||||
|
||||
void LayerManagerDefault::addLayerTypeCoreOnly(std::unique_ptr<mbgl::LayerFactory> layerFactory) {
|
||||
addLayerType(std::move(layerFactory));
|
||||
}
|
||||
|
||||
LayerManagerDefault::LayerManagerDefault() {
|
||||
#if !defined(MBGL_LAYER_FILL_DISABLE_ALL)
|
||||
addLayerType(std::make_unique<FillLayerFactory>());
|
||||
@@ -90,7 +103,7 @@ void LayerManagerDefault::addLayerType(std::unique_ptr<LayerFactory> factory) {
|
||||
LayerFactory* LayerManagerDefault::getFactory(const mbgl::style::LayerTypeInfo* typeInfo) noexcept {
|
||||
assert(typeInfo);
|
||||
for (const auto& factory : factories) {
|
||||
if (factory->getTypeInfo() == typeInfo) {
|
||||
if (layerTypeInfoEquals(factory->getTypeInfo(), typeInfo)) {
|
||||
return factory.get();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,6 +189,7 @@ _IOS_APPLICATION_RESOURCES = [
|
||||
"app/LaunchScreen.storyboard",
|
||||
"app/simple_route.json",
|
||||
"app/fill_filter_style.json",
|
||||
"//platform/darwin:app/PluginLayerTestStyle.json",
|
||||
"//platform/darwin:test/amsterdam.geojson",
|
||||
] + glob([
|
||||
"app/*.lproj/**",
|
||||
|
||||
@@ -58,6 +58,7 @@ Powerful, free and open-source mapping toolkit with full control over data sourc
|
||||
|
||||
- <doc:CustomStyleLayerExample>
|
||||
- <doc:RenderingStatisticsHud>
|
||||
- <doc:PluginLayers>
|
||||
|
||||
### Other Articles
|
||||
|
||||
|
||||
148
platform/ios/MapLibre.docc/PluginLayers.md
Normal file
148
platform/ios/MapLibre.docc/PluginLayers.md
Normal file
@@ -0,0 +1,148 @@
|
||||
# Plugin Layers
|
||||
|
||||
Plugin Layers are a way to add layer types that render themselves into the style parsing engine at runtime. It is a way to dynamically link new layer types to the MapLibre core -- and styling language -- without having to compile them into the library itself.
|
||||
|
||||
Because the layers are bound at runtime, one or more different types of specialized layers can be added and different versions of layer types can be added.
|
||||
|
||||
Currently Plugin Layers are only available on iOS/Darwin using the Metal Rendering pipeline.
|
||||
|
||||
|
||||
## Creating a Plugin Layer
|
||||
|
||||
Plugin Layers can be created by creating a descendant from MLNPluginLayer and add the class type to an instance of MLNMapView. The layer is self describing (e.g. the layer type, what properties are available, etc) and that information will be registered with the MapLibre core. When the plugin layer type is found in the style, the core will automatically instantiate it, pass along initial properties and manage the rendering of the layer.
|
||||
|
||||
### Defining a layer's capabilities
|
||||
The newly created layer class should override the layerCapabilites class method. Please note that this is a class method and not an instance method, so make sure it's prefaced with a + and not a -.
|
||||
|
||||
The object that is returned from this method defines the layer type and the propeties that the layer expects.
|
||||
|
||||
The triangle example (platform/darwin/app/PluginLayerExampleMetalRendering.mm) defines it's layer type "plugin-layer-metal-rendering" and two paint properties (scale and fill-color). These properties can be expression based. It's important to define the type of property (single float or color) and a default value. The properties returned by layerCapabilities will correspond to any properties in the "paint": part of the style.
|
||||
|
||||
Initialization properties can also be added to the "properties": section of the layer style.
|
||||
|
||||
```objc
|
||||
+(MLNPluginLayerCapabilities *)layerCapabilities {
|
||||
|
||||
MLNPluginLayerCapabilities *tempResult = [[MLNPluginLayerCapabilities alloc] init];
|
||||
tempResult.layerID = @"plugin-layer-metal-rendering";
|
||||
tempResult.requiresPass3D = YES;
|
||||
|
||||
// Define the paint properties that this layer implements and
|
||||
// what types they are
|
||||
tempResult.layerProperties = @[
|
||||
// The scale property
|
||||
[MLNPluginLayerProperty propertyWithName:@"scale"
|
||||
propertyType:MLNPluginLayerPropertyTypeSingleFloat
|
||||
defaultValue:@(1.0)],
|
||||
|
||||
// The fill color property
|
||||
[MLNPluginLayerProperty propertyWithName:@"fill-color"
|
||||
propertyType:MLNPluginLayerPropertyTypeColor
|
||||
defaultValue:[UIColor blueColor]]
|
||||
|
||||
];
|
||||
|
||||
return tempResult;
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
For the triangle example, there are two initialization properties (offset-x and offset-y) for positioning and two paint properties, scale and fill-color. Here are three different instances of the plugin layer being added to the style. One that has a static scale (and uses the default color), one that has an expression based scale (and uses the default scale) and one that has an expression based scale and fill color.
|
||||
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "metal-rendering-layer-1",
|
||||
"type": "plugin-layer-metal-rendering",
|
||||
"properties": {
|
||||
"offset-x": -300,
|
||||
"offset-y": -300
|
||||
},
|
||||
"paint": {
|
||||
"scale": 2.5
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "metal-rendering-layer-2",
|
||||
"type": "plugin-layer-metal-rendering",
|
||||
"properties": {
|
||||
"offset-x": -300
|
||||
},
|
||||
"paint": {
|
||||
"scale": [
|
||||
"interpolate",
|
||||
[
|
||||
"linear"
|
||||
],
|
||||
[
|
||||
"zoom"
|
||||
],
|
||||
5,
|
||||
0.5,
|
||||
15,
|
||||
3
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "metal-rendering-layer-3",
|
||||
"type": "plugin-layer-metal-rendering",
|
||||
"properties": {
|
||||
"color1": "#DDAAFF",
|
||||
"offset-x": 300
|
||||
},
|
||||
"paint": {
|
||||
"scale": [
|
||||
"interpolate",
|
||||
[
|
||||
"linear"
|
||||
],
|
||||
[
|
||||
"zoom"
|
||||
],
|
||||
5,
|
||||
3,
|
||||
15,
|
||||
0.5
|
||||
],
|
||||
"fill-color": [
|
||||
"interpolate",
|
||||
[
|
||||
"linear"
|
||||
],
|
||||
[
|
||||
"zoom"
|
||||
],
|
||||
1,
|
||||
"#ff0000",
|
||||
22,
|
||||
"#00ff00"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||

|
||||
|
||||
## Example 2: GLTF Plugin Layer
|
||||
|
||||
In [this repository](https://github.com/AtlasProgramming/maplibre-gltf-models-plugin) you can find an example of a plugin layer that allows adding GLTF models. The following JSON is added to the style.
|
||||
|
||||
```
|
||||
{
|
||||
"id": "model-layer",
|
||||
"type": "hudhud::gltf-model-layer",
|
||||
"properties": {
|
||||
"model-source-resource":"models.json"
|
||||
},
|
||||
"paint": {
|
||||
"scale": 1.0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@Video(
|
||||
source: "gltfplugin.mp4",
|
||||
poster: "gltfplugin.png",
|
||||
alt: "GLTF Plugin in action.") {
|
||||
GLTF Plugin showing 3D models of the Arc de Triomphe and the Eifel Tower.
|
||||
}
|
||||
@@ -23,6 +23,11 @@
|
||||
#import "MLNMapView_Experimental.h"
|
||||
#import <objc/runtime.h>
|
||||
|
||||
// Plug In Examples
|
||||
#import "PluginLayerExample.h"
|
||||
#import "PluginLayerExampleMetalRendering.h"
|
||||
#import "MLNPluginStyleLayer.h"
|
||||
|
||||
static const CLLocationCoordinate2D WorldTourDestinations[] = {
|
||||
{ .latitude = 38.8999418, .longitude = -77.033996 },
|
||||
{ .latitude = 37.7884307, .longitude = -122.3998631 },
|
||||
@@ -270,10 +275,21 @@ CLLocationCoordinate2D randomWorldCoordinate(void) {
|
||||
return self;
|
||||
}
|
||||
|
||||
// This will add the plug-in layers. This is a demo of how
|
||||
// extensible layers for the style can be added to the map view
|
||||
-(void)addPluginLayers {
|
||||
|
||||
[self.mapView addPluginLayerType:[PluginLayerExample class]];
|
||||
[self.mapView addPluginLayerType:[PluginLayerExampleMetalRendering class]];
|
||||
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
[self addPluginLayers];
|
||||
|
||||
// Keep track of current map state and debug preferences,
|
||||
// saving and restoring when the application's state changes.
|
||||
self.currentState = [MBXStateManager sharedManager].currentState;
|
||||
@@ -2314,10 +2330,19 @@ CLLocationCoordinate2D randomWorldCoordinate(void) {
|
||||
self.styleNames = [NSMutableArray array];
|
||||
self.styleURLs = [NSMutableArray array];
|
||||
|
||||
|
||||
|
||||
|
||||
/// Style that does not require an `apiKey` nor any further configuration
|
||||
[self.styleNames addObject:@"MapLibre Basic"];
|
||||
[self.styleURLs addObject:[NSURL URLWithString:@"https://demotiles.maplibre.org/style.json"]];
|
||||
|
||||
/// This is hte same style as above but copied locally and the three instances of the metal plug-in layer added to the style
|
||||
/// Look for "type": "plugin-layer-metal-rendering" in the PluginLayerTestStyle.json for an example of how the layer is defined
|
||||
[self.styleNames addObject:@"MapLibre Basic - Local With Plugin"];
|
||||
NSURL *url = [[NSBundle mainBundle] URLForResource:@"PluginLayerTestStyle.json" withExtension:nil];
|
||||
[self.styleURLs addObject:url];
|
||||
|
||||
/// Add MapLibre Styles if an `apiKey` exists
|
||||
NSString* apiKey = [MLNSettings apiKey];
|
||||
if (apiKey.length)
|
||||
|
||||
@@ -11,7 +11,7 @@ MLN_IOS_SDK_HEADERS = [
|
||||
"src/MLNUserLocation.h",
|
||||
"src/MLNUserLocationAnnotationView.h",
|
||||
"src/MLNUserLocationAnnotationViewStyle.h",
|
||||
"src/MLNScaleBar.h",
|
||||
"src/MLNScaleBar.h"
|
||||
]
|
||||
|
||||
MLN_IOS_PUBLIC_HEADERS = [
|
||||
|
||||
@@ -19,6 +19,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@class MLNPolygon;
|
||||
@class MLNScaleBar;
|
||||
@class MLNShape;
|
||||
@class MLNPluginLayer;
|
||||
|
||||
@protocol MLNMapViewDelegate;
|
||||
@protocol MLNAnnotation;
|
||||
@@ -2252,6 +2253,11 @@ vertically on the map.
|
||||
*/
|
||||
- (void)triggerRepaint;
|
||||
|
||||
/**
|
||||
Adds a plug-in layer that is external to this library
|
||||
*/
|
||||
- (void)addPluginLayerType:(Class)pluginLayerClass;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -26,6 +26,13 @@
|
||||
#include <mbgl/util/run_loop.hpp>
|
||||
#include <mbgl/util/string.hpp>
|
||||
#include <mbgl/util/projection.hpp>
|
||||
#include <mbgl/layermanager/layer_manager.hpp>
|
||||
#include <mbgl/plugin/plugin_layer_factory.hpp>
|
||||
#include <mbgl/plugin/plugin_layer.hpp>
|
||||
#include <mbgl/plugin/plugin_layer_impl.hpp>
|
||||
#include <mbgl/renderer/paint_parameters.hpp>
|
||||
#include <mbgl/mtl/mtl_fwd.hpp>
|
||||
#include <mbgl/mtl/render_pass.hpp>
|
||||
|
||||
#import "Mapbox.h"
|
||||
#import "MLNShape_Private.h"
|
||||
@@ -72,6 +79,9 @@
|
||||
#import "MLNSettings_Private.h"
|
||||
#import "MLNActionJournalOptions_Private.h"
|
||||
#import "MLNMapProjection.h"
|
||||
#import "MLNPluginLayer.h"
|
||||
#import "MLNStyleLayerManager.h"
|
||||
#include "MLNPluginStyleLayer_Private.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
@@ -442,6 +452,9 @@ public:
|
||||
@property (nonatomic) CADisplayLink *displayLink;
|
||||
@property (nonatomic, assign) BOOL needsDisplayRefresh;
|
||||
|
||||
// Plugin Layers
|
||||
@property NSMutableArray *pluginLayers;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MLNMapView
|
||||
@@ -552,6 +565,12 @@ public:
|
||||
|
||||
if (options)
|
||||
{
|
||||
if (options.pluginLayers) {
|
||||
for (Class c in options.pluginLayers) {
|
||||
[self addPluginLayerType:c];
|
||||
}
|
||||
}
|
||||
|
||||
if (options.styleURL) {
|
||||
self.styleURL = options.styleURL;
|
||||
} else if (options.styleJSON) {
|
||||
@@ -7624,6 +7643,146 @@ static void *windowScreenContext = &windowScreenContext;
|
||||
_mbglMap->triggerRepaint();
|
||||
}
|
||||
|
||||
/**
|
||||
Adds a plug-in layer that is external to this library
|
||||
*/
|
||||
-(void)addPluginLayerType:(Class)pluginLayerClass {
|
||||
|
||||
auto layerManager = mbgl::LayerManager::get();
|
||||
auto darwinLayerManager = (mbgl::LayerManagerDarwin *)layerManager;
|
||||
|
||||
MLNPluginLayerCapabilities *capabilities = [pluginLayerClass layerCapabilities];
|
||||
|
||||
std::string layerType = [capabilities.layerID UTF8String];
|
||||
|
||||
// Default values
|
||||
mbgl::style::LayerTypeInfo::Source source = mbgl::style::LayerTypeInfo::Source::NotRequired;
|
||||
mbgl::style::LayerTypeInfo::TileKind tileKind = mbgl::style::LayerTypeInfo::TileKind::NotRequired;
|
||||
mbgl::style::LayerTypeInfo::FadingTiles fadingTiles = mbgl::style::LayerTypeInfo::FadingTiles::NotRequired;
|
||||
mbgl::style::LayerTypeInfo::Layout layout = mbgl::style::LayerTypeInfo::Layout::NotRequired;
|
||||
mbgl::style::LayerTypeInfo::CrossTileIndex crossTileIndex = mbgl::style::LayerTypeInfo::CrossTileIndex::NotRequired;
|
||||
|
||||
mbgl::style::LayerTypeInfo::Pass3D pass3D = mbgl::style::LayerTypeInfo::Pass3D::NotRequired;
|
||||
if (capabilities.requiresPass3D) {
|
||||
pass3D = mbgl::style::LayerTypeInfo::Pass3D::Required;
|
||||
}
|
||||
|
||||
auto factory = std::make_unique<mbgl::PluginLayerPeerFactory>(layerType,
|
||||
source,
|
||||
pass3D,
|
||||
layout,
|
||||
fadingTiles,
|
||||
crossTileIndex,
|
||||
tileKind);
|
||||
|
||||
__weak MLNMapView *weakMapView = self;
|
||||
|
||||
Class layerClass = pluginLayerClass;
|
||||
factory->setOnLayerCreatedEvent([layerClass, weakMapView, pluginLayerClass](mbgl::style::PluginLayer *pluginLayer) {
|
||||
|
||||
//NSLog(@"Creating Plugin Layer: %@", layerClass);
|
||||
MLNPluginLayer *layer = [[layerClass alloc] init];
|
||||
if (!weakMapView.pluginLayers) {
|
||||
weakMapView.pluginLayers = [NSMutableArray array];
|
||||
}
|
||||
[weakMapView.pluginLayers addObject:layer];
|
||||
|
||||
// Use weak here so there isn't a retain cycle
|
||||
MLNPluginLayer *weakPlugInLayer = layer;
|
||||
|
||||
pluginLayer->_platformReference = (__bridge void *)layer;
|
||||
|
||||
MLNPluginLayerCapabilities *capabilities = [pluginLayerClass layerCapabilities];
|
||||
auto pluginLayerImpl = (mbgl::style::PluginLayer::Impl *)pluginLayer->baseImpl.get();
|
||||
auto & pm = pluginLayerImpl->_propertyManager;
|
||||
for (MLNPluginLayerProperty *property in capabilities.layerProperties) {
|
||||
mbgl::style::PluginLayerProperty *p = new mbgl::style::PluginLayerProperty();
|
||||
switch (property.propertyType) {
|
||||
case MLNPluginLayerPropertyTypeSingleFloat:
|
||||
p->_propertyType = mbgl::style::PluginLayerProperty::PropertyType::SingleFloat;
|
||||
p->_defaultSingleFloatValue = property.singleFloatDefaultValue;
|
||||
break;
|
||||
case MLNPluginLayerPropertyTypeColor:
|
||||
{
|
||||
p->_propertyType = mbgl::style::PluginLayerProperty::PropertyType::Color;
|
||||
if (property.colorDefaultValue) {
|
||||
CGFloat r, g, b, a;
|
||||
[property.colorDefaultValue getRed:&r green:&g blue:&b alpha:&a];
|
||||
p->_defaultColorValue = mbgl::Color(r, g, b, a);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
p->_propertyType = mbgl::style::PluginLayerProperty::PropertyType::Unknown;
|
||||
break;
|
||||
}
|
||||
p->_propertyName = [property.propertyName UTF8String];
|
||||
pm.addProperty(p);
|
||||
}
|
||||
|
||||
// Set the render function
|
||||
auto renderFunction = [weakPlugInLayer, weakMapView](mbgl::PaintParameters& paintParameters){
|
||||
|
||||
const mbgl::mtl::RenderPass& renderPass = static_cast<mbgl::mtl::RenderPass&>(*paintParameters.renderPass);
|
||||
id<MTLRenderCommandEncoder> encoder = (__bridge id<MTLRenderCommandEncoder>)renderPass.getMetalEncoder().get();
|
||||
|
||||
MLNMapView *strongMapView = weakMapView;
|
||||
|
||||
const mbgl::TransformState& state = paintParameters.state;
|
||||
|
||||
MLNPluginLayerDrawingContext drawingContext;
|
||||
drawingContext.size = CGSizeMake(state.getSize().width,
|
||||
state.getSize().height);
|
||||
drawingContext.centerCoordinate = CLLocationCoordinate2DMake(state.getLatLng().latitude(),
|
||||
state.getLatLng().longitude());
|
||||
drawingContext.zoomLevel = state.getZoom();
|
||||
drawingContext.direction = mbgl::util::rad2deg(-state.getBearing());
|
||||
drawingContext.pitch = state.getPitch();
|
||||
drawingContext.fieldOfView = state.getFieldOfView();
|
||||
mbgl::mat4 projMatrix;
|
||||
state.getProjMatrix(projMatrix);
|
||||
drawingContext.projectionMatrix = MLNMatrix4Make(projMatrix);
|
||||
|
||||
//NSLog(@"Rendering");
|
||||
|
||||
// Call update with the scene state variables
|
||||
[weakPlugInLayer onUpdateLayer:drawingContext];
|
||||
|
||||
// Call render
|
||||
[weakPlugInLayer onRenderLayer:strongMapView
|
||||
renderEncoder:encoder];
|
||||
};
|
||||
|
||||
// Set the lambdas
|
||||
//auto pluginLayerImpl = (mbgl::style::PluginLayer::Impl *)pluginLayer->baseImpl.get();
|
||||
pluginLayerImpl->setRenderFunction(renderFunction);
|
||||
|
||||
// Set the update properties function
|
||||
pluginLayerImpl->setUpdatePropertiesFunction([weakPlugInLayer](const std::string & jsonProperties) {
|
||||
// Use autorelease pools in lambdas
|
||||
@autoreleasepool {
|
||||
// Just wrap the string with NSData so it can be run through properties
|
||||
NSData *d = [NSData dataWithBytesNoCopy:(void *)jsonProperties.data() length:jsonProperties.length() freeWhenDone:NO];
|
||||
NSError *error = nil;
|
||||
NSDictionary *properties = [NSJSONSerialization JSONObjectWithData:d
|
||||
options:0
|
||||
error:&error];
|
||||
if (error) {
|
||||
// TODO: What should we do here?
|
||||
}
|
||||
[weakPlugInLayer onUpdateLayerProperties:properties];
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// TODO: Same question as above. Do we ever want to have a core only layer type?
|
||||
// This could actually be something that we could set in the layer capabilities class
|
||||
darwinLayerManager->addLayerType(std::move(factory));
|
||||
//darwinLayerManager->addLayerTypeCoreOnly(std::move(factory));
|
||||
|
||||
}
|
||||
|
||||
- (NSArray<NSString*>*)getActionJournalLogFiles
|
||||
{
|
||||
const auto& actionJournal = _mbglMap->getActionJournal();
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
void LayerManager::addLayerTypeCoreOnly(std::unique_ptr<mbgl::LayerFactory>) {}
|
||||
|
||||
std::unique_ptr<style::Layer> LayerManager::createLayer(const std::string& type,
|
||||
const std::string& id,
|
||||
const style::conversion::Convertible& value,
|
||||
|
||||
84
src/mbgl/plugin/plugin_layer.cpp
Normal file
84
src/mbgl/plugin/plugin_layer.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
#include <mbgl/plugin/plugin_layer.hpp>
|
||||
#include <mbgl/plugin/plugin_layer_impl.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
|
||||
PluginLayer::PluginLayer(const std::string& layerID,
|
||||
const std::string& sourceID,
|
||||
const style::LayerTypeInfo layerTypeInfo,
|
||||
const std::string& layerProperties
|
||||
|
||||
//,const style::conversion::Convertible& layerProperties
|
||||
)
|
||||
: Layer(makeMutable<Impl>(layerID, sourceID, layerTypeInfo, layerProperties)) {}
|
||||
|
||||
PluginLayer::PluginLayer(Immutable<Impl> impl_)
|
||||
: Layer(std::move(impl_)) {}
|
||||
|
||||
PluginLayer::~PluginLayer() = default;
|
||||
|
||||
const PluginLayer::Impl& PluginLayer::impl() const {
|
||||
return static_cast<const Impl&>(*baseImpl);
|
||||
}
|
||||
|
||||
Mutable<PluginLayer::Impl> PluginLayer::mutableImpl() const {
|
||||
return makeMutable<Impl>(impl());
|
||||
}
|
||||
|
||||
std::unique_ptr<Layer> PluginLayer::cloneRef(const std::string& id_) const {
|
||||
auto impl_ = mutableImpl();
|
||||
impl_->id = id_;
|
||||
return std::make_unique<PluginLayer>(std::move(impl_));
|
||||
}
|
||||
|
||||
void PluginLayer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const {}
|
||||
|
||||
Value PluginLayer::serialize() const {
|
||||
auto result = Layer::serialize();
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<conversion::Error> PluginLayer::setPropertyInternal(const std::string& name,
|
||||
const conversion::Convertible& value) {
|
||||
auto i = static_cast<const mbgl::style::PluginLayer::Impl*>(baseImpl.get());
|
||||
auto pm = i->_propertyManager;
|
||||
|
||||
// The properties should be defined when the plugin layer is created
|
||||
PluginLayerProperty* property = pm.getProperty(name); // i->_propertyManager.getProperty(name);
|
||||
if (property == nullptr) {
|
||||
return Error{"layer doesn't support this property"};
|
||||
}
|
||||
|
||||
Error error;
|
||||
if (property->_propertyType == PluginLayerProperty::PropertyType::SingleFloat) {
|
||||
const auto& tempValue = convert<PropertyValue<float>>(value, error, false, false);
|
||||
if (!tempValue) {
|
||||
return error;
|
||||
}
|
||||
property->setSingleFloat(tempValue.value());
|
||||
} else if (property->_propertyType == PluginLayerProperty::PropertyType::Color) {
|
||||
const auto& tempValue = convert<PropertyValue<mbgl::Color>>(value, error, false, false);
|
||||
if (!tempValue) {
|
||||
return error;
|
||||
}
|
||||
property->setColor(tempValue.value());
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
StyleProperty PluginLayer::getProperty([[maybe_unused]] const std::string& name) const {
|
||||
// TODO: Implement this via callback
|
||||
// return getLayerProperty(*this, name);
|
||||
return {};
|
||||
}
|
||||
|
||||
Mutable<Layer::Impl> PluginLayer::mutableBaseImpl() const {
|
||||
return staticMutableCast<Layer::Impl>(mutableImpl());
|
||||
}
|
||||
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
45
src/mbgl/plugin/plugin_layer.hpp
Normal file
45
src/mbgl/plugin/plugin_layer.hpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/style/layer.hpp>
|
||||
#include <mbgl/renderer/paint_parameters.hpp>
|
||||
#include <mbgl/renderer/render_layer.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
namespace style {
|
||||
|
||||
class PluginLayer final : public Layer {
|
||||
public:
|
||||
PluginLayer(const std::string& layerID,
|
||||
const std::string& sourceID,
|
||||
const style::LayerTypeInfo layerTypeInfo,
|
||||
const std::string& layerProperties);
|
||||
~PluginLayer() override;
|
||||
|
||||
// Private implementation
|
||||
class Impl;
|
||||
const Impl& impl() const;
|
||||
|
||||
Mutable<Impl> mutableImpl() const;
|
||||
PluginLayer(Immutable<Impl>);
|
||||
std::unique_ptr<Layer> cloneRef(const std::string& id) const final;
|
||||
|
||||
public:
|
||||
using OnRenderLayer = std::function<void(PaintParameters&)>;
|
||||
using OnUpdateLayer = std::function<void(const LayerPrepareParameters&)>;
|
||||
using OnUpdateLayerProperties = std::function<void(const std::string& properties)>;
|
||||
|
||||
void* _platformReference = nullptr;
|
||||
|
||||
protected:
|
||||
std::optional<conversion::Error> setPropertyInternal(const std::string& name,
|
||||
const conversion::Convertible& value) final;
|
||||
|
||||
StyleProperty getProperty(const std::string& name) const final;
|
||||
Value serialize() const final;
|
||||
|
||||
Mutable<Layer::Impl> mutableBaseImpl() const final;
|
||||
};
|
||||
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
188
src/mbgl/plugin/plugin_layer_factory.cpp
Normal file
188
src/mbgl/plugin/plugin_layer_factory.cpp
Normal file
@@ -0,0 +1,188 @@
|
||||
#include <mbgl/plugin/plugin_layer_factory.hpp>
|
||||
#include <mbgl/plugin/plugin_layer.hpp>
|
||||
#include <mbgl/plugin/plugin_layer_impl.hpp>
|
||||
#include <mbgl/plugin/plugin_layer_render.hpp>
|
||||
#include <mbgl/style/conversion_impl.hpp>
|
||||
#include <mbgl/renderer/bucket.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
namespace plugins {
|
||||
|
||||
// This is really hacky, but wanted to do it here to discuss if the
|
||||
// const can be removed from teh regular LayerTypeInfo
|
||||
struct NonConstLayerTypeInfo {
|
||||
const char* type;
|
||||
enum class Source {
|
||||
Required,
|
||||
NotRequired
|
||||
} source;
|
||||
enum class Pass3D {
|
||||
Required,
|
||||
NotRequired
|
||||
} pass3d;
|
||||
enum class Layout {
|
||||
Required,
|
||||
NotRequired
|
||||
} layout;
|
||||
enum class FadingTiles {
|
||||
Required,
|
||||
NotRequired
|
||||
} fadingTiles;
|
||||
enum class CrossTileIndex {
|
||||
Required,
|
||||
NotRequired
|
||||
} crossTileIndex;
|
||||
enum class TileKind : uint8_t {
|
||||
Geometry,
|
||||
Raster,
|
||||
RasterDEM,
|
||||
NotRequired
|
||||
} tileKind;
|
||||
};
|
||||
|
||||
} // namespace plugins
|
||||
|
||||
style::LayerTypeInfo getDefaultInfo() {
|
||||
style::LayerTypeInfo tempResult = {.type = "unknown",
|
||||
.source = style::LayerTypeInfo::Source::Required,
|
||||
.pass3d = style::LayerTypeInfo::Pass3D::Required,
|
||||
.layout = style::LayerTypeInfo::Layout::NotRequired,
|
||||
.fadingTiles = style::LayerTypeInfo::FadingTiles::NotRequired,
|
||||
.crossTileIndex = style::LayerTypeInfo::CrossTileIndex::NotRequired,
|
||||
.tileKind = style::LayerTypeInfo::TileKind::Geometry};
|
||||
return tempResult;
|
||||
}
|
||||
|
||||
PluginLayerFactory::PluginLayerFactory(std::string& layerType,
|
||||
mbgl::style::LayerTypeInfo::Source source,
|
||||
mbgl::style::LayerTypeInfo::Pass3D pass3D,
|
||||
mbgl::style::LayerTypeInfo::Layout layout,
|
||||
mbgl::style::LayerTypeInfo::FadingTiles fadingTiles,
|
||||
mbgl::style::LayerTypeInfo::CrossTileIndex crossTileIndex,
|
||||
mbgl::style::LayerTypeInfo::TileKind tileKind)
|
||||
: _layerTypeInfo(getDefaultInfo()),
|
||||
_layerType(layerType) {
|
||||
_layerTypeInfo.type = layerType.c_str();
|
||||
plugins::NonConstLayerTypeInfo* lti = (plugins::NonConstLayerTypeInfo*)&_layerTypeInfo;
|
||||
lti->source = (plugins::NonConstLayerTypeInfo::Source)((int)source);
|
||||
lti->pass3d = (plugins::NonConstLayerTypeInfo::Pass3D)((int)pass3D);
|
||||
lti->layout = (plugins::NonConstLayerTypeInfo::Layout)((int)layout);
|
||||
lti->fadingTiles = (plugins::NonConstLayerTypeInfo::FadingTiles)((int)fadingTiles);
|
||||
lti->crossTileIndex = (plugins::NonConstLayerTypeInfo::CrossTileIndex)((int)crossTileIndex);
|
||||
lti->tileKind = (plugins::NonConstLayerTypeInfo::TileKind)((int)tileKind);
|
||||
}
|
||||
|
||||
const style::LayerTypeInfo* PluginLayerFactory::getTypeInfo() const noexcept {
|
||||
return &_layerTypeInfo;
|
||||
}
|
||||
|
||||
void jsonStringFromConvertible(const style::conversion::Convertible& value, std::string& output) {
|
||||
if (isObject(value)) {
|
||||
output.append("{");
|
||||
bool firstItem = true;
|
||||
eachMember(value,
|
||||
[&output, &firstItem](const std::string& name, const style::conversion::Convertible& paramValue)
|
||||
-> std::optional<style::conversion::Error> {
|
||||
if (!firstItem) {
|
||||
output.append(",");
|
||||
}
|
||||
firstItem = false;
|
||||
output.append("\"");
|
||||
output.append(name);
|
||||
output.append("\":");
|
||||
|
||||
jsonStringFromConvertible(paramValue, output);
|
||||
|
||||
return std::nullopt;
|
||||
});
|
||||
output.append("}");
|
||||
} else if (isArray(value)) {
|
||||
output.append("[");
|
||||
bool firstItem = true;
|
||||
|
||||
for (size_t i = 0; i < arrayLength(value); i++) {
|
||||
if (!firstItem) {
|
||||
output.append(",");
|
||||
}
|
||||
firstItem = false;
|
||||
|
||||
auto itemValue = arrayMember(value, i);
|
||||
jsonStringFromConvertible(itemValue, output);
|
||||
}
|
||||
|
||||
output.append("]");
|
||||
} else {
|
||||
auto v = toValue(value);
|
||||
if (auto iVal = v.value().getInt()) {
|
||||
std::string tempResult = std::to_string(*iVal);
|
||||
output.append(tempResult);
|
||||
} else if (auto uIVal = v.value().getUint()) {
|
||||
std::string tempResult = std::to_string(*uIVal);
|
||||
output.append(tempResult);
|
||||
|
||||
} else if (auto s = v.value().getString()) {
|
||||
output.append("\"");
|
||||
output.append(s->c_str());
|
||||
output.append("\"");
|
||||
|
||||
} else if (auto d = v.value().getDouble()) {
|
||||
output.append(std::to_string(*d));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<style::Layer> PluginLayerFactory::createLayer(const std::string& id,
|
||||
const style::conversion::Convertible& value) noexcept {
|
||||
std::string layerProperties;
|
||||
|
||||
if (auto memberValue = objectMember(value, "properties")) {
|
||||
jsonStringFromConvertible(*memberValue, layerProperties);
|
||||
if (isObject(*memberValue)) {
|
||||
eachMember(*memberValue,
|
||||
[]([[maybe_unused]] const std::string& name,
|
||||
[[maybe_unused]] const style::conversion::Convertible& paramValue)
|
||||
-> std::optional<style::conversion::Error> { return std::nullopt; });
|
||||
}
|
||||
}
|
||||
|
||||
std::string source = "source";
|
||||
|
||||
auto tempResult = std::unique_ptr<style::Layer>(new (std::nothrow)
|
||||
style::PluginLayer(id, source, _layerTypeInfo, layerProperties
|
||||
//,*customProperties
|
||||
));
|
||||
|
||||
if (_onLayerCreated != nullptr) {
|
||||
auto layerRaw = tempResult.get();
|
||||
auto pluginLayer = static_cast<mbgl::style::PluginLayer*>(layerRaw);
|
||||
_onLayerCreated(pluginLayer);
|
||||
auto pluginLayerImpl = (mbgl::style::PluginLayer::Impl*)pluginLayer->baseImpl.get();
|
||||
if (pluginLayerImpl->_updateLayerPropertiesFunction != nullptr) {
|
||||
pluginLayerImpl->_updateLayerPropertiesFunction(layerProperties);
|
||||
}
|
||||
}
|
||||
|
||||
return tempResult;
|
||||
}
|
||||
|
||||
std::unique_ptr<Bucket> PluginLayerFactory::createBucket(
|
||||
[[maybe_unused]] const BucketParameters& parameters,
|
||||
[[maybe_unused]] const std::vector<Immutable<style::LayerProperties>>& layers) noexcept {
|
||||
// Returning null for now. Not using buckets in plug-ins yet.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<RenderLayer> PluginLayerFactory::createRenderLayer(Immutable<style::Layer::Impl> impl) noexcept {
|
||||
auto localImpl = staticImmutableCast<style::PluginLayer::Impl>(impl);
|
||||
auto tempResult = std::make_unique<RenderPluginLayer>(staticImmutableCast<style::PluginLayer::Impl>(impl));
|
||||
tempResult->setRenderFunction(localImpl->_renderFunction);
|
||||
tempResult->setUpdateFunction(localImpl->_updateFunction);
|
||||
tempResult->setUpdatePropertiesFunction(localImpl->_updateLayerPropertiesFunction);
|
||||
return tempResult;
|
||||
}
|
||||
|
||||
} // namespace mbgl
|
||||
45
src/mbgl/plugin/plugin_layer_factory.hpp
Normal file
45
src/mbgl/plugin/plugin_layer_factory.hpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/layermanager/layer_factory.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <functional>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
namespace style {
|
||||
// Forward
|
||||
class PluginLayer;
|
||||
|
||||
} // namespace style
|
||||
|
||||
class PluginLayerFactory : public LayerFactory {
|
||||
public:
|
||||
PluginLayerFactory(std::string& layerType,
|
||||
mbgl::style::LayerTypeInfo::Source source,
|
||||
mbgl::style::LayerTypeInfo::Pass3D pass3D,
|
||||
mbgl::style::LayerTypeInfo::Layout layout,
|
||||
mbgl::style::LayerTypeInfo::FadingTiles fadingTiles,
|
||||
mbgl::style::LayerTypeInfo::CrossTileIndex crossTileIndex,
|
||||
mbgl::style::LayerTypeInfo::TileKind tileKind);
|
||||
|
||||
using OnLayerCreatedEvent = std::function<void(mbgl::style::PluginLayer* pluginLayer)>;
|
||||
void setOnLayerCreatedEvent(OnLayerCreatedEvent onLayerCreated) { _onLayerCreated = onLayerCreated; }
|
||||
|
||||
protected:
|
||||
const style::LayerTypeInfo* getTypeInfo() const noexcept final;
|
||||
std::unique_ptr<style::Layer> createLayer(const std::string& id,
|
||||
const style::conversion::Convertible& value) noexcept final;
|
||||
std::unique_ptr<Bucket> createBucket(const BucketParameters&,
|
||||
const std::vector<Immutable<style::LayerProperties>>&) noexcept final;
|
||||
std::unique_ptr<RenderLayer> createRenderLayer(Immutable<style::Layer::Impl>) noexcept final;
|
||||
|
||||
private:
|
||||
// These is the layer type info that is setup during factory creation and returned in the getTypeInfo method
|
||||
style::LayerTypeInfo _layerTypeInfo;
|
||||
std::string _layerType;
|
||||
|
||||
OnLayerCreatedEvent _onLayerCreated;
|
||||
};
|
||||
|
||||
} // namespace mbgl
|
||||
99
src/mbgl/plugin/plugin_layer_impl.cpp
Normal file
99
src/mbgl/plugin/plugin_layer_impl.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
#include <mbgl/plugin/plugin_layer_impl.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
|
||||
PluginLayer::Impl::Impl(std::string layerID,
|
||||
std::string sourceID,
|
||||
LayerTypeInfo layerTypeInfo,
|
||||
const std::string& layerProperties)
|
||||
: Layer::Impl(layerID, sourceID),
|
||||
_layerTypeInfo(layerTypeInfo),
|
||||
_layerProperties(layerProperties) {}
|
||||
|
||||
bool PluginLayer::Impl::hasLayoutDifference([[maybe_unused]] const Layer::Impl& other) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return this property as json
|
||||
std::string PluginLayerProperty::asJSON() {
|
||||
std::string tempResult;
|
||||
|
||||
if (_propertyType == PropertyType::SingleFloat) {
|
||||
tempResult = "\"" + _propertyName + "\":" + std::to_string(_singleFloatValue);
|
||||
} else if (_propertyType == PropertyType::Color) {
|
||||
// In RGBA format
|
||||
tempResult = "\"" + _propertyName + "\":\"" + _dataDrivenColorValue.stringify() + "\"";
|
||||
}
|
||||
|
||||
return tempResult;
|
||||
}
|
||||
|
||||
void PluginLayerProperty::setPropertyValue([[maybe_unused]] const conversion::Convertible& value) {}
|
||||
|
||||
const PropertyValue<float>& PluginLayerProperty::getSingleFloat() const {
|
||||
return _singleFloatProperty;
|
||||
}
|
||||
|
||||
void PluginLayerProperty::setSingleFloat(const PropertyValue<float>& value) {
|
||||
_singleFloatProperty = std::move(value);
|
||||
}
|
||||
|
||||
void PluginLayerProperty::setCurrentSingleFloatValue(float value) {
|
||||
_singleFloatValue = value;
|
||||
}
|
||||
|
||||
PluginLayerProperty* PluginLayerPropertyManager::getProperty(const std::string& propertyName) {
|
||||
if (_properties.count(propertyName) > 0) {
|
||||
return _properties[propertyName];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void PluginLayerPropertyManager::addProperty(PluginLayerProperty* property) {
|
||||
_properties[property->_propertyName] = property;
|
||||
}
|
||||
|
||||
std::string PluginLayerPropertyManager::propertiesAsJSON() {
|
||||
std::string tempResult = "{";
|
||||
|
||||
bool firstItem = true;
|
||||
for (auto d : _properties) {
|
||||
if (!firstItem) {
|
||||
tempResult.append(", ");
|
||||
}
|
||||
firstItem = false;
|
||||
tempResult.append(d.second->asJSON());
|
||||
}
|
||||
tempResult.append("}");
|
||||
|
||||
return tempResult;
|
||||
}
|
||||
|
||||
std::vector<PluginLayerProperty*> PluginLayerPropertyManager::getProperties() {
|
||||
std::vector<PluginLayerProperty*> tempResult;
|
||||
tempResult.reserve(_properties.size());
|
||||
for (auto it = _properties.begin(); it != _properties.end(); ++it) {
|
||||
tempResult.push_back(it->second);
|
||||
}
|
||||
return tempResult;
|
||||
}
|
||||
|
||||
const PropertyValue<mbgl::Color>& PluginLayerProperty::getColor() const {
|
||||
return _dataDrivenColorProperty;
|
||||
}
|
||||
|
||||
void PluginLayerProperty::setColor(const PropertyValue<mbgl::Color>& value) {
|
||||
_dataDrivenColorProperty = std::move(value);
|
||||
}
|
||||
|
||||
void PluginLayerProperty::setCurrentColorValue(mbgl::Color value) {
|
||||
_dataDrivenColorValue = value;
|
||||
}
|
||||
|
||||
namespace conversion {} // namespace conversion
|
||||
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
122
src/mbgl/plugin/plugin_layer_impl.hpp
Normal file
122
src/mbgl/plugin/plugin_layer_impl.hpp
Normal file
@@ -0,0 +1,122 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/style/layer_impl.hpp>
|
||||
#include <mbgl/plugin/plugin_layer.hpp>
|
||||
#include <mbgl/style/conversion_impl.hpp>
|
||||
#include <mbgl/style/types.hpp>
|
||||
#include <mbgl/style/layer_properties.hpp>
|
||||
#include <mbgl/style/layout_property.hpp>
|
||||
#include <mbgl/style/paint_property.hpp>
|
||||
#include <mbgl/style/properties.hpp>
|
||||
#include <mbgl/shaders/attributes.hpp>
|
||||
#include <mbgl/shaders/uniforms.hpp>
|
||||
#include <mbgl/style/property_value.hpp>
|
||||
#include <mbgl/style/conversion/property_value.hpp>
|
||||
#include <mbgl/util/color.hpp>
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
|
||||
using namespace conversion;
|
||||
|
||||
struct DataDrivenSingleFloatProperty : DataDrivenPaintProperty<float, attributes::width, uniforms::width> {
|
||||
static float defaultValue() { return 1.f; }
|
||||
};
|
||||
|
||||
struct DataDrivenColorProperty : DataDrivenPaintProperty<mbgl::Color, attributes::color, uniforms::color> {
|
||||
static mbgl::Color defaultValue() { return mbgl::Color::black(); }
|
||||
static constexpr auto expressionType() { return expression::type::ColorType{}; };
|
||||
using EvaluatorType = DataDrivenPropertyEvaluator<Color, false>;
|
||||
};
|
||||
|
||||
class PluginLayerProperty {
|
||||
public:
|
||||
enum class PropertyType {
|
||||
Unknown,
|
||||
SingleFloat,
|
||||
Color
|
||||
};
|
||||
|
||||
public:
|
||||
PropertyType _propertyType = PropertyType::Unknown;
|
||||
std::string _propertyName;
|
||||
void setPropertyValue(const conversion::Convertible& value);
|
||||
|
||||
public:
|
||||
const PropertyValue<float>& getSingleFloat() const;
|
||||
void setSingleFloat(const PropertyValue<float>& value);
|
||||
float _defaultSingleFloatValue = 1.0;
|
||||
float _singleFloatValue = 0;
|
||||
PropertyValue<float> _singleFloatProperty;
|
||||
void setCurrentSingleFloatValue(float value);
|
||||
|
||||
// Color
|
||||
const PropertyValue<mbgl::Color>& getColor() const;
|
||||
void setColor(const PropertyValue<mbgl::Color>& value);
|
||||
mbgl::Color _defaultColorValue = mbgl::Color::black();
|
||||
mbgl::Color _dataDrivenColorValue = mbgl::Color::black();
|
||||
PropertyValue<mbgl::Color> _dataDrivenColorProperty;
|
||||
void setCurrentColorValue(mbgl::Color value);
|
||||
|
||||
// Return this property as json
|
||||
std::string asJSON();
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
class PluginLayerPropertyManager {
|
||||
public:
|
||||
PluginLayerProperty* getProperty(const std::string& propertyName);
|
||||
void addProperty(PluginLayerProperty* property);
|
||||
|
||||
std::string propertiesAsJSON();
|
||||
|
||||
std::vector<PluginLayerProperty*> getProperties();
|
||||
|
||||
private:
|
||||
std::map<std::string, PluginLayerProperty*> _properties;
|
||||
};
|
||||
|
||||
class PluginLayer::Impl : public Layer::Impl {
|
||||
public:
|
||||
Impl(std::string layerID, std::string sourceID, LayerTypeInfo layerTypeInfo, const std::string& layerProperties);
|
||||
|
||||
using Layer::Impl::Impl;
|
||||
|
||||
bool hasLayoutDifference(const Layer::Impl&) const override;
|
||||
void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override;
|
||||
|
||||
const LayerTypeInfo* getTypeInfo() const noexcept final { return &_layerTypeInfo; }
|
||||
|
||||
void setRenderFunction(OnRenderLayer renderFunction) { _renderFunction = renderFunction; }
|
||||
|
||||
void setUpdateFunction(OnUpdateLayer updateFunction) { _updateFunction = updateFunction; }
|
||||
|
||||
void setUpdatePropertiesFunction(OnUpdateLayerProperties updateLayerPropertiesFunction) {
|
||||
_updateLayerPropertiesFunction = updateLayerPropertiesFunction;
|
||||
}
|
||||
|
||||
//! The property manager handles all of the custom properties for this layer type / instance
|
||||
PluginLayerPropertyManager _propertyManager;
|
||||
|
||||
//! Optional: Called when the layer is expected to render itself.
|
||||
OnRenderLayer _renderFunction;
|
||||
|
||||
//! Optional: Called when the layer is expected to update it's animations/etc.
|
||||
// TODO: Does this need to be here or can it be done via the render function. Potentially, we could
|
||||
// have this method called on a background thread/etc or use another way to parallalize work
|
||||
OnUpdateLayer _updateFunction;
|
||||
|
||||
//! Optional: Called when the layer properties change. The properties are passed as JSON for now
|
||||
OnUpdateLayerProperties _updateLayerPropertiesFunction;
|
||||
|
||||
private:
|
||||
LayerTypeInfo _layerTypeInfo;
|
||||
std::string _layerProperties;
|
||||
};
|
||||
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
22
src/mbgl/plugin/plugin_layer_properties.cpp
Normal file
22
src/mbgl/plugin/plugin_layer_properties.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
|
||||
#include <mbgl/plugin/plugin_layer_properties.hpp>
|
||||
#include <mbgl/plugin/plugin_layer_impl.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
|
||||
PluginLayerProperties::PluginLayerProperties(Immutable<PluginLayer::Impl> impl_)
|
||||
: LayerProperties(std::move(impl_)) {}
|
||||
|
||||
PluginLayerProperties::~PluginLayerProperties() = default;
|
||||
|
||||
unsigned long PluginLayerProperties::constantsMask() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
expression::Dependency PluginLayerProperties::getDependencies() const noexcept {
|
||||
return expression::Dependency::None;
|
||||
}
|
||||
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
21
src/mbgl/plugin/plugin_layer_properties.hpp
Normal file
21
src/mbgl/plugin/plugin_layer_properties.hpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/style/types.hpp>
|
||||
#include <mbgl/style/layer_properties.hpp>
|
||||
#include <mbgl/plugin/plugin_layer.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
|
||||
class PluginLayerProperties final : public LayerProperties {
|
||||
public:
|
||||
PluginLayerProperties(Immutable<PluginLayer::Impl>);
|
||||
~PluginLayerProperties() override;
|
||||
|
||||
unsigned long constantsMask() const override;
|
||||
|
||||
expression::Dependency getDependencies() const noexcept override;
|
||||
};
|
||||
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
199
src/mbgl/plugin/plugin_layer_render.cpp
Normal file
199
src/mbgl/plugin/plugin_layer_render.cpp
Normal file
@@ -0,0 +1,199 @@
|
||||
#include <mbgl/plugin/plugin_layer_render.hpp>
|
||||
#include <mbgl/gfx/context.hpp>
|
||||
#include <mbgl/gfx/drawable_tweaker.hpp>
|
||||
#include <mbgl/gfx/drawable_builder.hpp>
|
||||
#include <mbgl/renderer/paint_parameters.hpp>
|
||||
#if MLN_RENDER_BACKEND_METAL
|
||||
#include <mbgl/style/layers/mtl/custom_layer_render_parameters.hpp>
|
||||
#include <mbgl/mtl/render_pass.hpp>
|
||||
#else
|
||||
#include <mbgl/style/layers/custom_layer_render_parameters.hpp>
|
||||
#endif
|
||||
#include <mbgl/gfx/drawable.hpp>
|
||||
#include <mbgl/gfx/context.hpp>
|
||||
#include <mbgl/gfx/renderer_backend.hpp>
|
||||
#include <mbgl/style/properties.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace mbgl;
|
||||
|
||||
namespace mbgl {
|
||||
namespace gfx {
|
||||
class Drawable;
|
||||
}
|
||||
} // namespace mbgl
|
||||
|
||||
class RenderPluginLayerTweaker : public mbgl::gfx::DrawableTweaker {
|
||||
public:
|
||||
RenderPluginLayerTweaker(RenderPluginLayer* plugInRenderer)
|
||||
: _plugInRenderer(plugInRenderer) {}
|
||||
~RenderPluginLayerTweaker() override = default;
|
||||
|
||||
void init(mbgl::gfx::Drawable&) override;
|
||||
|
||||
void execute(mbgl::gfx::Drawable&, mbgl::PaintParameters&) override;
|
||||
|
||||
protected:
|
||||
RenderPluginLayer* _plugInRenderer = nullptr;
|
||||
};
|
||||
|
||||
void RenderPluginLayerTweaker::init([[maybe_unused]] mbgl::gfx::Drawable& drawablee) {
|
||||
|
||||
};
|
||||
|
||||
void RenderPluginLayerTweaker::execute([[maybe_unused]] mbgl::gfx::Drawable& drawable,
|
||||
[[maybe_unused]] mbgl::PaintParameters& paintParameters) {
|
||||
// custom drawing
|
||||
auto& context = paintParameters.context;
|
||||
context.resetState(paintParameters.depthModeForSublayer(0, mbgl::gfx::DepthMaskType::ReadOnly),
|
||||
paintParameters.colorModeForRenderPass());
|
||||
|
||||
#if MLN_RENDER_BACKEND_METAL
|
||||
const auto& mtlRenderPass = static_cast<mbgl::mtl::RenderPass*>(paintParameters.renderPass.get());
|
||||
mtlRenderPass->resetState();
|
||||
|
||||
style::mtl::CustomLayerRenderParameters parameters(paintParameters);
|
||||
#else
|
||||
style::CustomLayerRenderParameters parameters(paintParameters);
|
||||
#endif
|
||||
|
||||
_plugInRenderer->render(paintParameters);
|
||||
|
||||
// Reset the view back to our original one, just in case the CustomLayer
|
||||
// changed the viewport or Framebuffer.
|
||||
paintParameters.backend.getDefaultRenderable().getResource<gfx::RenderableResource>().bind();
|
||||
|
||||
context.setDirtyState();
|
||||
context.bindGlobalUniformBuffers(*paintParameters.renderPass);
|
||||
}
|
||||
|
||||
RenderPluginLayer::RenderPluginLayer(Immutable<style::PluginLayer::Impl> _impl)
|
||||
: RenderLayer(makeMutable<style::PluginLayerProperties>(std::move(_impl))) {}
|
||||
|
||||
RenderPluginLayer::~RenderPluginLayer() = default;
|
||||
|
||||
void RenderPluginLayer::markLayerRenderable([[maybe_unused]] bool willRender,
|
||||
[[maybe_unused]] UniqueChangeRequestVec& changes) {
|
||||
isRenderable = true;
|
||||
}
|
||||
|
||||
/// Generate any changes needed by the layer
|
||||
void RenderPluginLayer::update([[maybe_unused]] gfx::ShaderRegistry& shaderRegistery,
|
||||
[[maybe_unused]] gfx::Context& context,
|
||||
[[maybe_unused]] const TransformState& transformState,
|
||||
[[maybe_unused]] const std::shared_ptr<UpdateParameters>& updateParameters,
|
||||
[[maybe_unused]] const RenderTree& renderTree,
|
||||
[[maybe_unused]] UniqueChangeRequestVec& changes) {
|
||||
// create layer group
|
||||
if (!layerGroup) {
|
||||
if (auto layerGroup_ = context.createLayerGroup(layerIndex, /*initialCapacity=*/1, getID())) {
|
||||
setLayerGroup(std::move(layerGroup_), changes);
|
||||
}
|
||||
}
|
||||
|
||||
auto* localLayerGroup = static_cast<LayerGroup*>(layerGroup.get());
|
||||
|
||||
// TODO: Implement this
|
||||
bool hostChanged = false;
|
||||
|
||||
// create drawable
|
||||
if (localLayerGroup->getDrawableCount() == 0 || hostChanged) {
|
||||
localLayerGroup->clearDrawables();
|
||||
|
||||
// create tweaker
|
||||
auto tweaker = std::make_shared<RenderPluginLayerTweaker>(this);
|
||||
|
||||
// create empty drawable using a builder
|
||||
std::unique_ptr<gfx::DrawableBuilder> builder = context.createDrawableBuilder(getID());
|
||||
auto& drawable = builder->getCurrentDrawable(true);
|
||||
drawable->setIsCustom(true);
|
||||
drawable->setRenderPass(RenderPass::Translucent);
|
||||
|
||||
// assign tweaker to drawable
|
||||
drawable->addTweaker(tweaker);
|
||||
|
||||
// add drawable to layer group
|
||||
localLayerGroup->addDrawable(std::move(drawable));
|
||||
++stats.drawablesAdded;
|
||||
}
|
||||
}
|
||||
|
||||
void RenderPluginLayer::upload([[maybe_unused]] gfx::UploadPass& uploadPass) {}
|
||||
|
||||
void RenderPluginLayer::render(PaintParameters& paintParameters) {
|
||||
if (_renderFunction) {
|
||||
_renderFunction(paintParameters);
|
||||
}
|
||||
}
|
||||
|
||||
void RenderPluginLayer::prepare(const LayerPrepareParameters& layerParameters) {
|
||||
if (_updateFunction) {
|
||||
_updateFunction(layerParameters);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Private methods
|
||||
void RenderPluginLayer::transition([[maybe_unused]] const TransitionParameters& parameters) {
|
||||
// Called when switching between styles
|
||||
}
|
||||
|
||||
void RenderPluginLayer::evaluate(const PropertyEvaluationParameters& parameters) {
|
||||
auto i = static_cast<const style::PluginLayer::Impl*>(baseImpl.get());
|
||||
|
||||
auto pm = i->_propertyManager;
|
||||
for (auto property : pm.getProperties()) {
|
||||
if (property->_propertyType == style::PluginLayerProperty::PropertyType::SingleFloat) {
|
||||
auto& f = property->getSingleFloat();
|
||||
using Evaluator = typename style::DataDrivenSingleFloatProperty::EvaluatorType;
|
||||
auto df = property->_defaultSingleFloatValue;
|
||||
auto newF = f.evaluate(Evaluator(parameters, df), parameters.now);
|
||||
auto v = newF.constant().value();
|
||||
property->setCurrentSingleFloatValue(v);
|
||||
} else if (property->_propertyType == style::PluginLayerProperty::PropertyType::Color) {
|
||||
auto& f = property->getColor();
|
||||
using Evaluator = typename style::DataDrivenColorProperty::EvaluatorType;
|
||||
auto df = property->_defaultColorValue;
|
||||
auto newF = f.evaluate(Evaluator(parameters, df), parameters.now);
|
||||
auto v = newF.constant().value();
|
||||
property->setCurrentColorValue(v);
|
||||
}
|
||||
}
|
||||
|
||||
std::string jsonProperties = pm.propertiesAsJSON();
|
||||
|
||||
i->_updateLayerPropertiesFunction(jsonProperties);
|
||||
}
|
||||
|
||||
bool RenderPluginLayer::hasTransition() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RenderPluginLayer::hasCrossfade() const {
|
||||
return false;
|
||||
}
|
||||
bool RenderPluginLayer::queryIntersectsFeature([[maybe_unused]] const GeometryCoordinates&,
|
||||
[[maybe_unused]] const GeometryTileFeature&,
|
||||
[[maybe_unused]] float,
|
||||
[[maybe_unused]] const TransformState&,
|
||||
[[maybe_unused]] float,
|
||||
[[maybe_unused]] const mat4&,
|
||||
[[maybe_unused]] const FeatureState&) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void RenderPluginLayer::layerChanged([[maybe_unused]] const TransitionParameters& parameters,
|
||||
[[maybe_unused]] const Immutable<style::Layer::Impl>& impl,
|
||||
[[maybe_unused]] UniqueChangeRequestVec& changes) {}
|
||||
|
||||
/// Remove all drawables for the tile from the layer group
|
||||
/// @return The number of drawables actually removed.
|
||||
std::size_t RenderPluginLayer::removeTile([[maybe_unused]] RenderPass, [[maybe_unused]] const OverscaledTileID&) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Remove all the drawables for tiles
|
||||
/// @return The number of drawables actually removed.
|
||||
std::size_t RenderPluginLayer::removeAllDrawables() {
|
||||
return 0;
|
||||
}
|
||||
72
src/mbgl/plugin/plugin_layer_render.hpp
Normal file
72
src/mbgl/plugin/plugin_layer_render.hpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/gfx/offscreen_texture.hpp>
|
||||
#include <mbgl/renderer/render_layer.hpp>
|
||||
#include <mbgl/plugin/plugin_layer_impl.hpp>
|
||||
#include <mbgl/plugin/plugin_layer_properties.hpp>
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class RenderPluginLayer final : public RenderLayer {
|
||||
public:
|
||||
explicit RenderPluginLayer(Immutable<style::PluginLayer::Impl>);
|
||||
~RenderPluginLayer() override;
|
||||
|
||||
void markLayerRenderable(bool willRender, UniqueChangeRequestVec& changes) override;
|
||||
|
||||
/// Generate any changes needed by the layer
|
||||
void update(gfx::ShaderRegistry&,
|
||||
gfx::Context&,
|
||||
const TransformState&,
|
||||
const std::shared_ptr<UpdateParameters>&,
|
||||
const RenderTree&,
|
||||
UniqueChangeRequestVec&) override;
|
||||
|
||||
void prepare(const LayerPrepareParameters&) override;
|
||||
|
||||
void upload(gfx::UploadPass&) override;
|
||||
void render(PaintParameters&) override;
|
||||
|
||||
void setRenderFunction(style::PluginLayer::OnRenderLayer renderFunction) { _renderFunction = renderFunction; }
|
||||
void setUpdateFunction(style::PluginLayer::OnUpdateLayer updateFunction) { _updateFunction = updateFunction; }
|
||||
void setUpdatePropertiesFunction(style::PluginLayer::OnUpdateLayerProperties updateLayerPropertiesFunction) {
|
||||
_updateLayerPropertiesFunction = updateLayerPropertiesFunction;
|
||||
}
|
||||
|
||||
private:
|
||||
void transition(const TransitionParameters&) override;
|
||||
void evaluate(const PropertyEvaluationParameters&) override;
|
||||
bool hasTransition() const override;
|
||||
bool hasCrossfade() const override;
|
||||
bool queryIntersectsFeature(const GeometryCoordinates&,
|
||||
const GeometryTileFeature&,
|
||||
float,
|
||||
const TransformState&,
|
||||
float,
|
||||
const mat4&,
|
||||
const FeatureState&) const override;
|
||||
void updateColorRamp();
|
||||
|
||||
void layerChanged(const TransitionParameters& parameters,
|
||||
const Immutable<style::Layer::Impl>& impl,
|
||||
UniqueChangeRequestVec& changes) override;
|
||||
|
||||
/// Remove all drawables for the tile from the layer group
|
||||
/// @return The number of drawables actually removed.
|
||||
std::size_t removeTile(RenderPass, const OverscaledTileID&) override;
|
||||
|
||||
/// Remove all the drawables for tiles
|
||||
/// @return The number of drawables actually removed.
|
||||
std::size_t removeAllDrawables() override;
|
||||
|
||||
// The render methods
|
||||
style::PluginLayer::OnRenderLayer _renderFunction = nullptr;
|
||||
|
||||
style::PluginLayer::OnUpdateLayer _updateFunction = nullptr;
|
||||
|
||||
style::PluginLayer::OnUpdateLayerProperties _updateLayerPropertiesFunction = nullptr;
|
||||
};
|
||||
|
||||
} // namespace mbgl
|
||||
@@ -12,6 +12,14 @@
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
|
||||
// Added this to support plugins and that their LayerTypeInfo isn't the same point
|
||||
// across the board
|
||||
bool layerTypeInfoEquals(const mbgl::style::LayerTypeInfo* one, const mbgl::style::LayerTypeInfo* other) {
|
||||
return ((strcmp(one->type, other->type) == 0) && (one->source == other->source) && (one->pass3d == other->pass3d) &&
|
||||
(one->layout == other->layout) && (one->fadingTiles == other->fadingTiles) &&
|
||||
(one->crossTileIndex == other->crossTileIndex) && (one->tileKind == other->tileKind));
|
||||
};
|
||||
|
||||
static_assert(mbgl::underlying_type(Tile::Kind::Geometry) == mbgl::underlying_type(LayerTypeInfo::TileKind::Geometry),
|
||||
"tile kind error");
|
||||
static_assert(mbgl::underlying_type(Tile::Kind::Raster) == mbgl::underlying_type(LayerTypeInfo::TileKind::Raster),
|
||||
|
||||
@@ -18,6 +18,7 @@ add_library(
|
||||
${PROJECT_SOURCE_DIR}/test/math/minmax.test.cpp
|
||||
${PROJECT_SOURCE_DIR}/test/math/wrap.test.cpp
|
||||
${PROJECT_SOURCE_DIR}/test/platform/settings.test.cpp
|
||||
${PROJECT_SOURCE_DIR}/test/plugin/plugin.test.cpp
|
||||
${PROJECT_SOURCE_DIR}/test/renderer/image_manager.test.cpp
|
||||
${PROJECT_SOURCE_DIR}/test/renderer/pattern_atlas.test.cpp
|
||||
${PROJECT_SOURCE_DIR}/test/renderer/shader_registry.test.cpp
|
||||
|
||||
236
test/plugin/plugin.test.cpp
Normal file
236
test/plugin/plugin.test.cpp
Normal file
@@ -0,0 +1,236 @@
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
#include <mbgl/test/util.hpp>
|
||||
#include <mbgl/test/stub_file_source.hpp>
|
||||
#include <mbgl/test/stub_map_observer.hpp>
|
||||
#include <mbgl/test/fake_file_source.hpp>
|
||||
#include <mbgl/test/fixture_log_observer.hpp>
|
||||
#include <mbgl/test/map_adapter.hpp>
|
||||
|
||||
#include <mbgl/gfx/backend_scope.hpp>
|
||||
#include <mbgl/gfx/headless_frontend.hpp>
|
||||
#include <mbgl/gfx/shader_registry.hpp>
|
||||
#include <mbgl/map/map_options.hpp>
|
||||
#include <mbgl/math/log2.hpp>
|
||||
#include <mbgl/renderer/renderer.hpp>
|
||||
#include <mbgl/renderer/update_parameters.hpp>
|
||||
#include <mbgl/storage/file_source_manager.hpp>
|
||||
#include <mbgl/storage/main_resource_loader.hpp>
|
||||
#include <mbgl/storage/network_status.hpp>
|
||||
#include <mbgl/storage/online_file_source.hpp>
|
||||
#include <mbgl/storage/resource_options.hpp>
|
||||
#include <mbgl/style/expression/dsl.hpp>
|
||||
#include <mbgl/style/image_impl.hpp>
|
||||
#include <mbgl/style/image.hpp>
|
||||
#include <mbgl/style/layers/background_layer.hpp>
|
||||
#include <mbgl/style/layers/background_layer.hpp>
|
||||
#include <mbgl/style/layers/fill_layer.hpp>
|
||||
#include <mbgl/style/layers/raster_layer.hpp>
|
||||
#include <mbgl/style/layers/symbol_layer.hpp>
|
||||
#include <mbgl/style/sources/custom_geometry_source.hpp>
|
||||
#include <mbgl/style/sources/geojson_source.hpp>
|
||||
#include <mbgl/style/sources/image_source.hpp>
|
||||
#include <mbgl/style/sources/vector_source.hpp>
|
||||
#include <mbgl/style/style_impl.hpp>
|
||||
#include <mbgl/style/style.hpp>
|
||||
#include <mbgl/util/async_task.hpp>
|
||||
#include <mbgl/util/client_options.hpp>
|
||||
#include <mbgl/util/color.hpp>
|
||||
#include <mbgl/util/image.hpp>
|
||||
#include <mbgl/util/io.hpp>
|
||||
#include <mbgl/util/logging.hpp>
|
||||
#include <mbgl/util/run_loop.hpp>
|
||||
|
||||
#include <mbgl/layermanager/layer_manager.hpp>
|
||||
#include <mbgl/plugin/plugin_layer_factory.hpp>
|
||||
#include <mbgl/plugin/plugin_layer.hpp>
|
||||
#include <mbgl/plugin/plugin_layer_impl.hpp>
|
||||
|
||||
#include <atomic>
|
||||
|
||||
using namespace mbgl;
|
||||
using namespace mbgl::style;
|
||||
using namespace std::literals::string_literals;
|
||||
|
||||
template <class FileSource = StubFileSource, class Frontend = HeadlessFrontend>
|
||||
class MapTest {
|
||||
public:
|
||||
util::RunLoop runLoop;
|
||||
std::shared_ptr<FileSource> fileSource;
|
||||
StubMapObserver observer;
|
||||
Frontend frontend;
|
||||
MapAdapter map;
|
||||
|
||||
MapTest(float pixelRatio = 1, MapMode mode = MapMode::Static)
|
||||
: fileSource(std::make_shared<FileSource>(ResourceOptions::Default(), ClientOptions())),
|
||||
frontend(pixelRatio),
|
||||
map(frontend,
|
||||
observer,
|
||||
fileSource,
|
||||
MapOptions().withMapMode(mode).withSize(frontend.getSize()).withPixelRatio(pixelRatio)) {}
|
||||
|
||||
explicit MapTest(MapOptions options)
|
||||
: fileSource(std::make_shared<FileSource>()),
|
||||
frontend(options.pixelRatio()),
|
||||
map(frontend, observer, fileSource, options.withSize(frontend.getSize())) {}
|
||||
|
||||
template <typename T = FileSource>
|
||||
MapTest(const std::string& cachePath,
|
||||
const std::string& assetPath,
|
||||
float pixelRatio = 1,
|
||||
MapMode mode = MapMode::Static,
|
||||
typename std::enable_if_t<std::is_same_v<T, MainResourceLoader>>* = nullptr)
|
||||
: fileSource(std::make_shared<T>(ResourceOptions().withCachePath(cachePath).withAssetPath(assetPath),
|
||||
ClientOptions())),
|
||||
frontend(pixelRatio),
|
||||
map(frontend,
|
||||
observer,
|
||||
fileSource,
|
||||
MapOptions().withMapMode(mode).withSize(frontend.getSize()).withPixelRatio(pixelRatio)) {}
|
||||
|
||||
template <typename T = FileSource>
|
||||
MapTest(const ResourceOptions& resourceOptions,
|
||||
const ClientOptions& clientOptions = ClientOptions(),
|
||||
float pixelRatio = 1,
|
||||
MapMode mode = MapMode::Static)
|
||||
: fileSource(std::make_shared<T>(resourceOptions, clientOptions)),
|
||||
frontend(pixelRatio),
|
||||
map(frontend,
|
||||
observer,
|
||||
fileSource,
|
||||
MapOptions().withMapMode(mode).withSize(frontend.getSize()).withPixelRatio(pixelRatio)) {}
|
||||
};
|
||||
|
||||
TEST(Plugin, PluginLayerProperty) {
|
||||
bool _singleFloatValid = false;
|
||||
bool _colorValid = false;
|
||||
|
||||
std::cout << "Test: Plugin.PluginLayerProperty\n";
|
||||
|
||||
{
|
||||
PluginLayerProperty p;
|
||||
p._propertyType = PluginLayerProperty::PropertyType::SingleFloat;
|
||||
p._singleFloatValue = 1.0;
|
||||
auto json = p.asJSON();
|
||||
std::string testValue = R"("":1.000000)";
|
||||
std::cout << " _singleFloatValid: " << json << ";|" << testValue << "|\n";
|
||||
_singleFloatValid = json == testValue;
|
||||
EXPECT_STREQ(testValue.c_str(), json.c_str());
|
||||
}
|
||||
|
||||
{
|
||||
PluginLayerProperty p;
|
||||
p._propertyType = PluginLayerProperty::PropertyType::Color;
|
||||
p._dataDrivenColorValue = Color(1, 1, 1, 1);
|
||||
auto json = p.asJSON();
|
||||
std::string testValue = R"JSON("":"rgba(255,255,255,1)")JSON";
|
||||
std::cout << " _colorValid: |" << json << "|;|" << testValue << "|\n";
|
||||
_colorValid = json == testValue;
|
||||
EXPECT_STREQ(testValue.c_str(), json.c_str());
|
||||
}
|
||||
|
||||
ASSERT_TRUE(_colorValid);
|
||||
ASSERT_TRUE(_singleFloatValid);
|
||||
}
|
||||
|
||||
TEST(Plugin, PluginLayerPropertyManager) {
|
||||
PluginLayerPropertyManager pm;
|
||||
PluginLayerProperty* p1 = new PluginLayerProperty();
|
||||
p1->_propertyType = PluginLayerProperty::PropertyType::SingleFloat;
|
||||
p1->_propertyName = "float";
|
||||
p1->_singleFloatValue = 1.0;
|
||||
pm.addProperty(p1);
|
||||
|
||||
PluginLayerProperty* p2 = new PluginLayerProperty();
|
||||
p2->_propertyType = PluginLayerProperty::PropertyType::Color;
|
||||
p2->_propertyName = "color";
|
||||
p2->_dataDrivenColorValue = Color(1, 1, 1, 1);
|
||||
pm.addProperty(p2);
|
||||
|
||||
auto json = pm.propertiesAsJSON();
|
||||
|
||||
std::cout << "Test: Plugin.PluginLayerPropertyManager\n";
|
||||
std::string testValue = R"JSON({"color":"rgba(255,255,255,1)", "float":1.000000})JSON";
|
||||
std::cout << " JSON: |" << json << "|;|" << testValue << "|\n";
|
||||
EXPECT_STREQ(testValue.c_str(), json.c_str());
|
||||
|
||||
bool _jsonValid = (json == testValue);
|
||||
ASSERT_TRUE(_jsonValid);
|
||||
}
|
||||
|
||||
TEST(Plugin, PluginLayer) {
|
||||
bool _layerCreated = false;
|
||||
bool _initialPropertiesFound = false;
|
||||
bool _layerRendered = false;
|
||||
bool _paintPropertiesFound = false;
|
||||
|
||||
// Create the plug-in layer type
|
||||
std::string layerType = "plugin-layer-test";
|
||||
auto pluginLayerFactory = std::make_unique<PluginLayerFactory>(
|
||||
layerType,
|
||||
mbgl::style::LayerTypeInfo::Source::NotRequired,
|
||||
mbgl::style::LayerTypeInfo::Pass3D::NotRequired,
|
||||
mbgl::style::LayerTypeInfo::Layout::NotRequired,
|
||||
mbgl::style::LayerTypeInfo::FadingTiles::NotRequired,
|
||||
mbgl::style::LayerTypeInfo::CrossTileIndex::NotRequired,
|
||||
mbgl::style::LayerTypeInfo::TileKind::NotRequired);
|
||||
pluginLayerFactory->setOnLayerCreatedEvent(
|
||||
[&_layerRendered, &_layerCreated, &_initialPropertiesFound, &_paintPropertiesFound](
|
||||
mbgl::style::PluginLayer* pluginLayer) {
|
||||
// std::cout << "On Layer Created\n";
|
||||
_layerCreated = true;
|
||||
|
||||
auto pluginLayerImpl = (mbgl::style::PluginLayer::Impl*)pluginLayer->baseImpl.get();
|
||||
auto& pm = pluginLayerImpl->_propertyManager;
|
||||
PluginLayerProperty* p = new PluginLayerProperty();
|
||||
p->_propertyName = "scale";
|
||||
p->_propertyType = PluginLayerProperty::PropertyType::SingleFloat;
|
||||
p->_defaultSingleFloatValue = 1.0;
|
||||
pm.addProperty(p);
|
||||
pluginLayerImpl->setRenderFunction([&_layerRendered]([[maybe_unused]] PaintParameters& paintParameters) {
|
||||
// std::cout << "On Layer Rendered\n";
|
||||
// std::cout << " Paint Properties.currentLayer: " << paintParameters.currentLayer <<
|
||||
// "\n";
|
||||
_layerRendered = true;
|
||||
});
|
||||
|
||||
pluginLayerImpl->setUpdatePropertiesFunction(
|
||||
[&_initialPropertiesFound, &_paintPropertiesFound](const std::string& properties) {
|
||||
// std::cout << "On Layer Update Properties\n";
|
||||
// std::cout << " -> " << properties << "\n";
|
||||
|
||||
auto propertiesCheck = R"({"custom-property1":"custom-property-value1"})";
|
||||
if (properties == propertiesCheck) {
|
||||
_initialPropertiesFound = true;
|
||||
}
|
||||
if (properties.find("scale") > 0) {
|
||||
_paintPropertiesFound = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
auto lm = LayerManager::get();
|
||||
lm->addLayerTypeCoreOnly(std::move(pluginLayerFactory));
|
||||
|
||||
MapTest<> test{1, MapMode::Continuous};
|
||||
test.map.getStyle().loadJSON(R"STYLE({
|
||||
"version": 8,
|
||||
"layers": [{
|
||||
"id": "plugin-layer-1",
|
||||
"type": "plugin-layer-test",
|
||||
"properties": {
|
||||
"custom-property1":"custom-property-value1"
|
||||
},
|
||||
"paint": {"scale": 1.0 }
|
||||
}]
|
||||
})STYLE");
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
test.runLoop.runOnce();
|
||||
}
|
||||
|
||||
ASSERT_TRUE(_layerCreated);
|
||||
ASSERT_TRUE(_initialPropertiesFound);
|
||||
ASSERT_TRUE(_layerRendered);
|
||||
ASSERT_TRUE(_paintPropertiesFound);
|
||||
}
|
||||
Reference in New Issue
Block a user