From d866b289cc7337c491ebde9546360a2a52ec4a0a Mon Sep 17 00:00:00 2001 From: Alex Cristici Date: Tue, 21 Jul 2026 17:56:36 +0300 Subject: [PATCH] Add fill extrusion style property that enables rounded corners for extruded buildings (Core feature) (#4343) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Bart Louwers --- include/mbgl/shaders/gl/fill_extrusion.hpp | 27 +++-- .../shaders/gl/fill_extrusion_pattern.hpp | 35 +++--- include/mbgl/shaders/mtl/fill_extrusion.hpp | 94 ++++++++------- include/mbgl/shaders/shader_defines.hpp | 4 +- .../mbgl/shaders/vulkan/fill_extrusion.hpp | 107 +++++++++-------- .../mbgl/shaders/webgpu/fill_extrusion.hpp | 59 ++++----- .../style/layers/fill_extrusion_layer.hpp | 6 + .../cpp/style/layers/fill_extrusion_layer.cpp | 13 ++ .../cpp/style/layers/fill_extrusion_layer.hpp | 2 + .../style/layers/FillExtrusionLayer.java | 16 +++ .../android/style/layers/PropertyFactory.java | 20 ++++ .../testapp/style/FillExtrusionLayerTest.java | 13 ++ scripts/style-spec-reference/v8.json | 14 +++ shaders/fill_extrusion.vertex.glsl | 21 ++-- shaders/fill_extrusion_pattern.vertex.glsl | 27 +++-- .../fill_extrusion_layer_factory.cpp | 5 +- .../buckets/fill_extrusion_bucket.cpp | 113 +++++++++++------- .../buckets/fill_extrusion_bucket.hpp | 53 ++++---- .../layers/render_fill_extrusion_layer.cpp | 17 ++- src/mbgl/shaders/attributes.hpp | 8 +- src/mbgl/shaders/gl/shader_info.cpp | 6 +- src/mbgl/shaders/mtl/fill_extrusion.cpp | 24 ++-- src/mbgl/shaders/vulkan/fill_extrusion.cpp | 24 ++-- src/mbgl/shaders/webgpu/fill_extrusion.cpp | 24 ++-- .../style/layers/fill_extrusion_layer.cpp | 37 +++++- .../layers/fill_extrusion_layer_impl.hpp | 2 +- .../fill_extrusion_layer_properties.cpp | 2 +- .../fill_extrusion_layer_properties.hpp | 9 ++ src/mbgl/tile/geometry_tile_data.cpp | 72 +++++++++++ src/mbgl/tile/geometry_tile_data.hpp | 6 + src/mbgl/util/math.hpp | 10 ++ 31 files changed, 579 insertions(+), 291 deletions(-) diff --git a/include/mbgl/shaders/gl/fill_extrusion.hpp b/include/mbgl/shaders/gl/fill_extrusion.hpp index 10a7d912f..8686000c0 100644 --- a/include/mbgl/shaders/gl/fill_extrusion.hpp +++ b/include/mbgl/shaders/gl/fill_extrusion.hpp @@ -9,7 +9,8 @@ template <> struct ShaderSource { static constexpr const char* name = "FillExtrusionShader"; static constexpr const char* vertex = R"(layout (location = 0) in vec2 a_pos; -layout (location = 1) in vec4 a_normal_ed; +layout (location = 1) in vec2 a_decimals_ed; +layout (location = 2) in vec2 a_normal2d; out vec4 v_color; layout (std140) uniform FillExtrusionDrawableUBO { @@ -52,13 +53,13 @@ layout (std140) uniform FillExtrusionPropsUBO { }; #ifndef HAS_UNIFORM_u_base -layout (location = 2) in highp vec2 a_base; +layout (location = 3) in highp vec2 a_base; #endif #ifndef HAS_UNIFORM_u_height -layout (location = 3) in highp vec2 a_height; +layout (location = 4) in highp vec2 a_height; #endif #ifndef HAS_UNIFORM_u_color -layout (location = 4) in highp vec4 a_color; +layout (location = 5) in highp vec4 a_color; #endif void main() { @@ -78,14 +79,16 @@ highp vec4 color = unpack_mix_color(a_color, u_color_t); highp vec4 color = u_color; #endif - vec3 normal = a_normal_ed.xyz; + vec3 normal = vec3(a_normal2d / 16384.0, a_normal2d.x == 0.0 && a_normal2d.y == 0.0 ? 1.0 : 0.0); base = max(0.0, base); height = max(0.0, height); - float t = mod(normal.x, 2.0); + float t = mod(a_decimals_ed.x, 2.0); + float z = t > 0.0 ? height : base; + vec2 decimals = unpack_float(floor(a_decimals_ed.x / 2.0)) / 128.0; - gl_Position = u_matrix * vec4(a_pos, t > 0.0 ? height : base, 1); + gl_Position = u_matrix * vec4(a_pos + decimals, z, 1); // Relative luminance (how dark/bright is the surface color?) float colorvalue = color.r * 0.2126 + color.g * 0.7152 + color.b * 0.0722; @@ -97,7 +100,7 @@ highp vec4 color = u_color; color += ambientlight; // Calculate cos(theta), where theta is the angle between surface normal and diffuse light ray - float directional = clamp(dot(normal / 16384.0, u_lightpos), 0.0, 1.0); + float directional = clamp(dot(normal, u_lightpos), 0.0, 1.0); // Adjust directional so that // the range of values for highlight/shading is narrower @@ -106,12 +109,12 @@ highp vec4 color = u_color; directional = mix((1.0 - u_lightintensity), max((1.0 - colorvalue + u_lightintensity), 1.0), directional); // Add gradient along z axis of side surfaces - if (normal.y != 0.0) { + if (normal.z == 0.0) { // This avoids another branching statement, but multiplies by a constant of 0.84 if no vertical gradient, // and otherwise calculates the gradient based on base + height - directional *= ( - (1.0 - u_vertical_gradient) + - (u_vertical_gradient * clamp((t + base) * pow(height / 150.0, 0.5), mix(0.7, 0.98, 1.0 - u_lightintensity), 1.0))); + float fMin = mix(0.7, 0.98, 1.0 - u_lightintensity); + float factor = clamp((t + base) * pow(height / 150.0, 0.5), fMin, 1.0); + directional *= (1.0 - u_vertical_gradient) + (u_vertical_gradient * factor); } // Assign final color based on surface + ambient light color, diffuse light directional, and light color diff --git a/include/mbgl/shaders/gl/fill_extrusion_pattern.hpp b/include/mbgl/shaders/gl/fill_extrusion_pattern.hpp index 11f7e40c4..c5ab62c88 100644 --- a/include/mbgl/shaders/gl/fill_extrusion_pattern.hpp +++ b/include/mbgl/shaders/gl/fill_extrusion_pattern.hpp @@ -9,7 +9,8 @@ template <> struct ShaderSource { static constexpr const char* name = "FillExtrusionPatternShader"; static constexpr const char* vertex = R"(layout (location = 0) in vec2 a_pos; -layout (location = 1) in vec4 a_normal_ed; +layout (location = 1) in vec2 a_decimals_ed; +layout (location = 2) in vec2 a_normal2d; out vec2 v_pos_a; out vec2 v_pos_b; @@ -67,19 +68,19 @@ layout (std140) uniform FillExtrusionPropsUBO { }; #ifndef HAS_UNIFORM_u_base -layout (location = 2) in lowp vec2 a_base; +layout (location = 3) in lowp vec2 a_base; out lowp float base; #endif #ifndef HAS_UNIFORM_u_height -layout (location = 3) in lowp vec2 a_height; +layout (location = 4) in lowp vec2 a_height; out lowp float height; #endif #ifndef HAS_UNIFORM_u_pattern_from -layout (location = 4) in mediump vec4 a_pattern_from; +layout (location = 5) in mediump vec4 a_pattern_from; out mediump vec4 pattern_from; #endif #ifndef HAS_UNIFORM_u_pattern_to -layout (location = 5) in mediump vec4 a_pattern_to; +layout (location = 6) in mediump vec4 a_pattern_to; out mediump vec4 pattern_to; #endif @@ -115,8 +116,8 @@ mediump vec4 pattern_to = u_pattern_to; float fromScale = u_from_scale; float toScale = u_to_scale; - vec3 normal = a_normal_ed.xyz; - float edgedistance = a_normal_ed.w; + vec3 normal = vec3(a_normal2d / 16384.0, a_normal2d.x == 0.0 && a_normal2d.y == 0.0 ? 1.0 : 0.0); + float edgedistance = a_decimals_ed.y; vec2 display_size_a = vec2((pattern_br_a.x - pattern_tl_a.x) / pixelRatio, (pattern_br_a.y - pattern_tl_a.y) / pixelRatio); vec2 display_size_b = vec2((pattern_br_b.x - pattern_tl_b.x) / pixelRatio, (pattern_br_b.y - pattern_tl_b.y) / pixelRatio); @@ -124,28 +125,28 @@ mediump vec4 pattern_to = u_pattern_to; base = max(0.0, base); height = max(0.0, height); - float t = mod(normal.x, 2.0); + float t = mod(a_decimals_ed.x, 2.0); float z = t > 0.0 ? height : base; + vec2 decimals = unpack_float(floor(a_decimals_ed.x / 2.0)) / 128.0; - gl_Position = u_matrix * vec4(a_pos, z, 1); + gl_Position = u_matrix * vec4(a_pos + decimals, z, 1); - vec2 pos = normal.x == 1.0 && normal.y == 0.0 && normal.z == 16384.0 - ? a_pos // extrusion top - : vec2(edgedistance, z * u_height_factor); // extrusion side + // extrusion top or side + vec2 pos = normal.z == 1.0 ? a_pos : vec2(edgedistance, z * u_height_factor); v_pos_a = get_pattern_pos(u_pixel_coord_upper, u_pixel_coord_lower, fromScale * display_size_a, tileRatio, pos); v_pos_b = get_pattern_pos(u_pixel_coord_upper, u_pixel_coord_lower, toScale * display_size_b, tileRatio, pos); v_lighting = vec4(0.0, 0.0, 0.0, 1.0); - float directional = clamp(dot(normal / 16383.0, u_lightpos), 0.0, 1.0); + float directional = clamp(dot(normal, u_lightpos), 0.0, 1.0); directional = mix((1.0 - u_lightintensity), max((0.5 + u_lightintensity), 1.0), directional); - if (normal.y != 0.0) { + if (normal.z == 0.0) { // This avoids another branching statement, but multiplies by a constant of 0.84 if no vertical gradient, // and otherwise calculates the gradient based on base + height - directional *= ( - (1.0 - u_vertical_gradient) + - (u_vertical_gradient * clamp((t + base) * pow(height / 150.0, 0.5), mix(0.7, 0.98, 1.0 - u_lightintensity), 1.0))); + float fMin = mix(0.7, 0.98, 1.0 - u_lightintensity); + float factor = clamp((t + base) * pow(height / 150.0, 0.5), fMin, 1.0); + directional *= (1.0 - u_vertical_gradient) + (u_vertical_gradient * factor); } v_lighting.rgb += clamp(directional * u_lightcolor, mix(vec3(0.0), vec3(0.3), 1.0 - u_lightcolor), vec3(1.0)); diff --git a/include/mbgl/shaders/mtl/fill_extrusion.hpp b/include/mbgl/shaders/mtl/fill_extrusion.hpp index 6b8ba851d..efddecf20 100644 --- a/include/mbgl/shaders/mtl/fill_extrusion.hpp +++ b/include/mbgl/shaders/mtl/fill_extrusion.hpp @@ -69,7 +69,7 @@ struct ShaderSource { static constexpr auto vertexMainFunction = "vertexMain"; static constexpr auto fragmentMainFunction = "fragmentMain"; - static const std::array attributes; + static const std::array attributes; static constexpr std::array instanceAttributes{}; static const std::array textures; @@ -78,15 +78,16 @@ struct ShaderSource { struct VertexStage { short2 pos [[attribute(0)]]; + ushort2 decimals_ed [[attribute(1)]]; #if !defined(HAS_UNIFORM_u_color) - float4 color [[attribute(1)]]; + float4 color [[attribute(2)]]; #endif #if !defined(HAS_UNIFORM_u_base) - float base [[attribute(2)]]; + float base [[attribute(3)]]; #endif #if !defined(HAS_UNIFORM_u_height) - float height [[attribute(3)]]; + float height [[attribute(4)]]; #endif }; @@ -121,7 +122,9 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], const float3 normal = float3(0.0, 0.0, 1.0); const float t = 1.0; const float z = (t != 0.0) ? height : base; // TODO: This would come out wrong on GL for negative values, check it... - const float4 position = drawable.matrix * float4(float2(vertx.pos), z, 1); + const float2 decimals = unpack_float(float(vertx.decimals_ed.x / 2)) / 128.0; + + const float4 position = drawable.matrix * float4(float2(vertx.pos) + decimals, z, 1); #if defined(OVERDRAW_INSPECTOR) return { @@ -154,7 +157,7 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], float directional = mix(minDirectional, maxDirectional, directionalFraction); // Add gradient along z axis of side surfaces - if (normal.y != 0.0) { + if (normal.z == 0.0) { // This avoids another branching statement, but multiplies by a constant of 0.84 if no // vertical gradient, and otherwise calculates the gradient based on base + height // TODO: If we're optimizing to the level of avoiding branches, we should pre-compute @@ -212,7 +215,7 @@ struct VertexStage { struct OutlineInstance { short2 pos; - ushort2 ed_discard; + ushort2 decimals_ed; }; struct FragmentStage { @@ -232,7 +235,8 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], uint instanceID [[ instance_id ]], device const OutlineInstance* outline [[buffer(fillExtrusionUBOCount + 1)]]) { - if (outline[instanceID].ed_discard.y) { + bool isDiscarded = glMod(float(outline[instanceID].decimals_ed.x), 2.0) > 0.0; + if (isDiscarded) { return { .position = float4(0.0), .color = half4(0.0), @@ -252,18 +256,15 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], const auto height = max(unpack_mix_float(vertx.height, drawable.height_t), 0.0); #endif - float2 p1 = float2(outline[instanceID + 1].pos); - float2 p2 = float2(outline[instanceID + 0].pos); - float2 perp = p1 - p2; - float magnitude = sqrt(perp.x * perp.x + perp.y * perp.y); - if (magnitude > 0) { - perp = perp / magnitude; - } + const float2 p1 = float2(outline[instanceID + 0].pos) + unpack_float(float(outline[instanceID + 0].decimals_ed.x / 2)) / 128.0; + const float2 p2 = float2(outline[instanceID + 1].pos) + unpack_float(float(outline[instanceID + 1].decimals_ed.x / 2)) / 128.0; + const float2 edgevector = normalize(p2 - p1); - const float3 normal = float3(-perp.y, perp.x, 0.0); + const float3 normal = float3(-edgevector.y, edgevector.x, 0.0); const float t = float(vertx.pos.y); const float z = (t != 0.0) ? height : base; // TODO: This would come out wrong on GL for negative values, check it... - const float4 position = drawable.matrix * float4(float2(outline[instanceID + vertx.pos.x].pos), z, 1); + + const float4 position = drawable.matrix * float4(vertx.pos.x == 0.0 ? p1 : p2, z, 1); #if defined(OVERDRAW_INSPECTOR) return { @@ -296,7 +297,7 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], float directional = mix(minDirectional, maxDirectional, directionalFraction); // Add gradient along z axis of side surfaces - if (normal.y != 0.0) { + if (normal.z == 0.0) { // This avoids another branching statement, but multiplies by a constant of 0.84 if no // vertical gradient, and otherwise calculates the gradient based on base + height // TODO: If we're optimizing to the level of avoiding branches, we should pre-compute @@ -331,7 +332,7 @@ struct ShaderSource attributes; + static const std::array attributes; static constexpr std::array instanceAttributes{}; static const std::array textures; @@ -340,18 +341,19 @@ struct ShaderSource 0.0; + if (isDiscarded) { return { .position = float4(0.0), .lighting = float4(0.0), @@ -600,19 +607,16 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], const auto height = max(unpack_mix_float(vertx.height, drawable.height_t), 0.0); #endif - float2 p1 = float2(outline[instanceID + 1].pos); - float2 p2 = float2(outline[instanceID + 0].pos); - float2 perp = p1 - p2; - float magnitude = sqrt(perp.x * perp.x + perp.y * perp.y); - if (magnitude > 0) { - perp = perp / magnitude; - } + const float2 p1 = float2(outline[instanceID + 0].pos) + unpack_float(float(outline[instanceID + 0].decimals_ed.x / 2)) / 128.0; + const float2 p2 = float2(outline[instanceID + 1].pos) + unpack_float(float(outline[instanceID + 1].decimals_ed.x / 2)) / 128.0; + const float2 edgevector = normalize(p2 - p1); - const float3 normal = float3(-perp.y, perp.x, 0.0); - const float edgedistance = outline[instanceID + 1 - vertx.pos.x].ed_discard.x; + const float3 normal = float3(-edgevector.y, edgevector.x, 0.0); + const float edgedistance = outline[instanceID + 1 - vertx.pos.x].decimals_ed.y; const float t = float(vertx.pos.y); const float z = (t != 0.0) ? height : base; // TODO: This would come out wrong on GL for negative values, check it... - const float4 position = drawable.matrix * float4(float2(outline[instanceID + vertx.pos.x].pos), z, 1); + + const float4 position = drawable.matrix * float4(vertx.pos.x == 0.0 ? p1 : p2, z, 1); #if defined(OVERDRAW_INSPECTOR) return { @@ -660,12 +664,14 @@ FragmentStage vertex vertexMain(thread const VertexStage vertx [[stage_in]], float directional = clamp(dot(normal, props.light_position_base.xyz), 0.0, 1.0); directional = mix((1.0 - props.light_intensity), max((0.5 + props.light_intensity), 1.0), directional); - if (normal.y != 0.0) { + if (normal.z == 0.0) { // This avoids another branching statement, but multiplies by a constant of 0.84 if no vertical gradient, // and otherwise calculates the gradient based on base + height - directional *= ( - (1.0 - props.vertical_gradient) + - (props.vertical_gradient * clamp((t + base) * pow(height / 150.0, 0.5), mix(0.7, 0.98, 1.0 - props.light_intensity), 1.0))); + // TODO: If we're optimizing to the level of avoiding branches, we should pre-compute + // the square root when height is a uniform. + const float fMin = mix(0.7, 0.98, 1.0 - props.light_intensity); + const float factor = clamp((t + base) * pow(height / 150.0, 0.5), fMin, 1.0); + directional *= (1.0 - props.vertical_gradient) + (props.vertical_gradient * factor); } lighting.rgb += clamp(directional * props.light_color_pad.rgb, mix(float3(0.0), float3(0.3), 1.0 - props.light_color_pad.rgb), float3(1.0)); diff --git a/include/mbgl/shaders/shader_defines.hpp b/include/mbgl/shaders/shader_defines.hpp index b51584a0d..862085b49 100644 --- a/include/mbgl/shaders/shader_defines.hpp +++ b/include/mbgl/shaders/shader_defines.hpp @@ -450,12 +450,12 @@ enum { enum { idFillExtrusionPosVertexAttribute, + idFillExtrusionDecimalsEdAttribute, #if MLN_USE_FILL_EXTRUSION_INSTANCING idFillExtrusionOutlinePosAttribute, - idFillExtrusionEdDiscardAttribute, #else - idFillExtrusionNormalEdVertexAttribute, + idFillExtrusionNormal2DVertexAttribute, #endif // Data driven diff --git a/include/mbgl/shaders/vulkan/fill_extrusion.hpp b/include/mbgl/shaders/vulkan/fill_extrusion.hpp index 77594ad28..5038e5167 100644 --- a/include/mbgl/shaders/vulkan/fill_extrusion.hpp +++ b/include/mbgl/shaders/vulkan/fill_extrusion.hpp @@ -19,7 +19,7 @@ template <> struct ShaderSource { static constexpr const char* name = "FillExtrusionShader"; - static const std::array attributes; + static const std::array attributes; static constexpr std::array instanceAttributes{}; static const std::array textures; @@ -27,17 +27,18 @@ struct ShaderSource { static constexpr auto vertex = R"( layout(location = 0) in ivec2 in_position; +layout(location = 1) in uvec2 in_decimals_ed; #if !defined(HAS_UNIFORM_u_color) -layout(location = 1) in vec4 in_color; +layout(location = 2) in vec4 in_color; #endif #if !defined(HAS_UNIFORM_u_base) -layout(location = 2) in vec2 in_base; +layout(location = 3) in vec2 in_base; #endif #if !defined(HAS_UNIFORM_u_height) -layout(location = 3) in vec2 in_height; +layout(location = 4) in vec2 in_height; #endif layout(push_constant) uniform Constants { @@ -102,9 +103,10 @@ void main() { const vec3 normal = vec3(0.0, 0.0, 1.0); const float t = 1.0; - const float z = t != 0.0 ? height : base; + const float z = t > 0.0 ? height : base; + const vec2 decimals = unpack_float(floor(in_decimals_ed.x / 2)) / 128.0; - gl_Position = drawable.matrix * vec4(in_position, z, 1.0); + gl_Position = drawable.matrix * vec4(in_position + decimals, z, 1.0); applySurfaceTransform(); #if defined(OVERDRAW_INSPECTOR) @@ -130,7 +132,7 @@ void main() { float directional = mix(minDirectional, maxDirectional, directionalFraction); // Add gradient along z axis of side surfaces - if (normal.y != 0.0) { + if (normal.z == 0.0) { // This avoids another branching statement, but multiplies by a constant of 0.84 if no // vertical gradient, and otherwise calculates the gradient based on base + height // TODO: If we're optimizing to the level of avoiding branches, we should pre-compute @@ -226,7 +228,7 @@ layout(set = LAYER_SET_INDEX, binding = idFillExtrusionPropsUBO) uniform FillExt struct OutlineInstance { int pos; - uint ed_discard; + uint decimals_ed; }; layout(std430, set = DRAWABLE_UBO_SET_INDEX, binding = idFillExtrusionInstancedDrawableUBO) readonly buffer FillExtrusionInstanceVector { @@ -238,9 +240,10 @@ layout(location = 0) out mediump vec4 frag_color; void main() { const vec2 instancePos = unpack_int(instanceVector.instance[gl_InstanceIndex].pos); - const vec2 instanceEd = unpack_uint(instanceVector.instance[gl_InstanceIndex].ed_discard); + const vec2 instanceDecimalsEd = unpack_uint(instanceVector.instance[gl_InstanceIndex].decimals_ed); - if (instanceEd.y > 0.0) { + bool isDiscarded = mod(instanceDecimalsEd.x, 2.0) > 0.0; + if (isDiscarded) { gl_Position = vec4(0.0); frag_color = vec4(0.0); return; @@ -265,16 +268,18 @@ void main() { #else vec4 color = unpack_mix_color(in_color, drawable.color_t); #endif + const vec2 nextInstancePos = unpack_int(instanceVector.instance[gl_InstanceIndex + 1].pos); + const vec2 nextInstanceDecimalsEd = unpack_uint(instanceVector.instance[gl_InstanceIndex + 1].decimals_ed); - const vec2 p1 = unpack_int(instanceVector.instance[gl_InstanceIndex + 1].pos); - const vec2 p2 = unpack_int(instanceVector.instance[gl_InstanceIndex + 0].pos); - const vec2 perp = normalize(p1 - p2); + const vec2 p1 = instancePos + unpack_float(floor(instanceDecimalsEd.x / 2)) / 128.0; + const vec2 p2 = nextInstancePos + unpack_float(floor(nextInstanceDecimalsEd.x / 2)) / 128.0; + const vec2 edgevector = normalize(p2 - p1); - const vec3 normal = vec3(-perp.y, perp.x, 0.0); + const vec3 normal = vec3(-edgevector.y, edgevector.x, 0.0); const float t = float(in_position.y); - const float z = t != 0.0 ? height : base; + const float z = t > 0.0 ? height : base; - gl_Position = drawable.matrix * vec4(unpack_int(instanceVector.instance[gl_InstanceIndex + in_position.x].pos), z, 1.0); + gl_Position = drawable.matrix * vec4(in_position.x == 0.0 ? p1 : p2, z, 1.0); applySurfaceTransform(); #if defined(OVERDRAW_INSPECTOR) @@ -300,7 +305,7 @@ void main() { float directional = mix(minDirectional, maxDirectional, directionalFraction); // Add gradient along z axis of side surfaces - if (normal.y != 0.0) { + if (normal.z == 0.0) { // This avoids another branching statement, but multiplies by a constant of 0.84 if no // vertical gradient, and otherwise calculates the gradient based on base + height // TODO: If we're optimizing to the level of avoiding branches, we should pre-compute @@ -336,7 +341,7 @@ template <> struct ShaderSource { static constexpr const char* name = "FillExtrusionPatternShader"; - static const std::array attributes; + static const std::array attributes; static constexpr std::array instanceAttributes{}; static const std::array textures; @@ -344,21 +349,22 @@ struct ShaderSource 0.0 ? height : base; + const vec2 decimals = unpack_float(floor(in_decimals_ed.x / 2)) / 128.0; - gl_Position = drawable.matrix * vec4(in_position, z, 1.0); + gl_Position = drawable.matrix * vec4(in_position + decimals, z, 1.0); applySurfaceTransform(); #if defined(OVERDRAW_INSPECTOR) @@ -487,13 +494,14 @@ void main() { float directional = clamp(dot(normal, props.light_position_base.xyz), 0.0, 1.0); directional = mix((1.0 - props.light_intensity), max((0.5 + props.light_intensity), 1.0), directional); - if (normal.y != 0.0) { - // This avoids another branching statement, but multiplies by a constant of 0.84 if no vertical gradient, - // and otherwise calculates the gradient based on base + height - directional *= ( - (1.0 - props.vertical_gradient) + - (props.vertical_gradient * clamp((t + base) * pow(height / 150.0, 0.5), mix(0.7, 0.98, 1.0 - props.light_intensity), 1.0))); - } + if (normal.z == 0.0) { + // This avoids another branching statement, but multiplies by a constant of 0.84 if no + // vertical gradient, and otherwise calculates the gradient based on base + height + // TODO: If we're optimizing to the level of avoiding branches, we should pre-compute + // the square root when height is a uniform. + const float fMin = mix(0.7, 0.98, 1.0 - props.light_intensity); + const float factor = clamp((t + base) * pow(height / 150.0, 0.5), fMin, 1.0); + directional *= (1.0 - props.vertical_gradient) + (props.vertical_gradient * factor); } lighting.rgb += clamp(directional * props.light_color_pad.rgb, mix(vec3(0.0), vec3(0.3), 1.0 - props.light_color_pad.rgb), vec3(1.0)); lighting *= props.opacity; @@ -652,7 +660,7 @@ layout(std140, set = LAYER_SET_INDEX, binding = idFillExtrusionDrawableUBO) read struct OutlineInstance { int pos; - uint ed_discard; + uint decimals_ed; }; layout(std430, set = DRAWABLE_UBO_SET_INDEX, binding = idFillExtrusionInstancedDrawableUBO) readonly buffer FillExtrusionInstanceVector { @@ -699,9 +707,10 @@ layout(location = 4) out mediump vec4 frag_pattern_to; void main() { const vec2 instancePos = unpack_int(instanceVector.instance[gl_InstanceIndex].pos); - const vec2 instanceEd = unpack_uint(instanceVector.instance[gl_InstanceIndex].ed_discard); + const vec2 instanceDecimalsEd = unpack_uint(instanceVector.instance[gl_InstanceIndex].decimals_ed); - if (instanceEd.y > 0.0) { + bool isDiscarded = mod(instanceDecimalsEd.x, 2.0) > 0.0; + if (isDiscarded) { gl_Position = vec4(0.0); return; } @@ -720,17 +729,19 @@ void main() { #else const float height = max(unpack_mix_float(in_height, drawable.height_t), 0.0); #endif + const vec2 nextInstancePos = unpack_int(instanceVector.instance[gl_InstanceIndex + 1].pos); + const vec2 nextInstanceDecimalsEd = unpack_uint(instanceVector.instance[gl_InstanceIndex + 1].decimals_ed); - const vec2 p1 = unpack_int(instanceVector.instance[gl_InstanceIndex + 1].pos); - const vec2 p2 = unpack_int(instanceVector.instance[gl_InstanceIndex + 0].pos); - const vec2 perp = normalize(p1 - p2); + const vec2 p1 = instancePos + unpack_float(floor(instanceDecimalsEd.x / 2)) / 128.0; + const vec2 p2 = nextInstancePos + unpack_float(floor(nextInstanceDecimalsEd.x / 2)) / 128.0; + const vec2 edgevector = normalize(p2 - p1); - const vec3 normal = vec3(-perp.y, perp.x, 0.0); - const float edgedistance = unpack_uint(instanceVector.instance[gl_InstanceIndex + 1 - in_position.x].ed_discard).x; + const vec3 normal = vec3(-edgevector.y, edgevector.x, 0.0); + const float edgedistance = in_position.x == 0.0 ? nextInstanceDecimalsEd.y : instanceDecimalsEd.y; const float t = float(in_position.y); - const float z = t != 0.0 ? height : base; + const float z = t > 0.0 ? height : base; - gl_Position = drawable.matrix * vec4(unpack_int(instanceVector.instance[gl_InstanceIndex + in_position.x].pos), z, 1.0); + gl_Position = drawable.matrix * vec4(in_position.x == 0.0 ? p1 : p2, z, 1.0); applySurfaceTransform(); #if defined(OVERDRAW_INSPECTOR) @@ -775,12 +786,14 @@ void main() { float directional = clamp(dot(normal, props.light_position_base.xyz), 0.0, 1.0); directional = mix((1.0 - props.light_intensity), max((0.5 + props.light_intensity), 1.0), directional); - if (normal.y != 0.0) { - // This avoids another branching statement, but multiplies by a constant of 0.84 if no vertical gradient, - // and otherwise calculates the gradient based on base + height - directional *= ( - (1.0 - props.vertical_gradient) + - (props.vertical_gradient * clamp((t + base) * pow(height / 150.0, 0.5), mix(0.7, 0.98, 1.0 - props.light_intensity), 1.0))); + if (normal.z == 0.0) { + // This avoids another branching statement, but multiplies by a constant of 0.84 if no + // vertical gradient, and otherwise calculates the gradient based on base + height + // TODO: If we're optimizing to the level of avoiding branches, we should pre-compute + // the square root when height is a uniform. + const float fMin = mix(0.7, 0.98, 1.0 - props.light_intensity); + const float factor = clamp((t + base) * pow(height / 150.0, 0.5), fMin, 1.0); + directional *= (1.0 - props.vertical_gradient) + (props.vertical_gradient * factor); } lighting.rgb += clamp(directional * props.light_color_pad.rgb, mix(vec3(0.0), vec3(0.3), 1.0 - props.light_color_pad.rgb), vec3(1.0)); diff --git a/include/mbgl/shaders/webgpu/fill_extrusion.hpp b/include/mbgl/shaders/webgpu/fill_extrusion.hpp index b0f4c5559..9e546ba43 100644 --- a/include/mbgl/shaders/webgpu/fill_extrusion.hpp +++ b/include/mbgl/shaders/webgpu/fill_extrusion.hpp @@ -10,22 +10,23 @@ namespace shaders { template <> struct ShaderSource { static constexpr const char* name = "FillExtrusionShader"; - static const std::array attributes; + static const std::array attributes; static constexpr std::array instanceAttributes{}; static const std::array textures; static constexpr auto vertex = R"( struct VertexInput { @location(3) position: vec2, - @location(4) normal_ed: vec4, + @location(4) decimals_ed: vec2, + @location(5) normal2d: vec2, #ifndef HAS_UNIFORM_u_color - @location(5) color: vec4, + @location(6) color: vec4, #endif #ifndef HAS_UNIFORM_u_base - @location(6) base: vec2, + @location(7) base: vec2, #endif #ifndef HAS_UNIFORM_u_height - @location(7) height: vec2, + @location(8) height: vec2, #endif }; @@ -88,11 +89,13 @@ fn main(in: VertexInput) -> VertexOutput { let heightValue = max(props.height, 0.0); #endif - let normal_i = vec3(f32(in.normal_ed.x), f32(in.normal_ed.y), f32(in.normal_ed.z)); - let t = glMod(normal_i.x, 2.0); - let z = select(baseValue, heightValue, t != 0.0); + let normal = vec3(vec2(in.normal2d) / 16384.0, select(0.0, 1.0, in.normal2d.x == 0 && in.normal2d.y == 0)); - out.position = drawable.matrix * vec4(f32(in.position.x), f32(in.position.y), z, 1.0); + let t = glMod(f32(in.decimals_ed.x), 2.0); + let z = select(baseValue, heightValue, t > 0.0); + let decimals = unpack_float(f32(in.decimals_ed.x / 2)) / 128.0; + + out.position = drawable.matrix * vec4(vec2(in.position) + decimals, z, 1.0); #ifdef OVERDRAW_INSPECTOR out.color = vec4(1.0, 1.0, 1.0, 1.0); @@ -107,13 +110,12 @@ fn main(in: VertexInput) -> VertexOutput { color = color + min(vec4(0.03, 0.03, 0.03, 1.0), vec4(1.0)); let luminance = dot(color.rgb, vec3(0.2126, 0.7152, 0.0722)); - let unitNormal = normal_i / 16384.0; - let directionalFraction = clamp(dot(unitNormal, props.light_position_base.xyz), 0.0, 1.0); + let directionalFraction = clamp(dot(normal, props.light_position_base.xyz), 0.0, 1.0); let minDirectional = 1.0 - props.light_intensity; let maxDirectional = max(1.0 - luminance + props.light_intensity, 1.0); var directional = mix(minDirectional, maxDirectional, directionalFraction); - if (normal_i.y != 0.0) { + if (normal.z == 0.0) { let gradientMin = mix(0.7, 0.98, 1.0 - props.light_intensity); let factor = clamp((t + baseValue) * pow(heightValue / 150.0, 0.5), gradientMin, 1.0); directional *= (1.0 - props.vertical_gradient) + props.vertical_gradient * factor; @@ -146,25 +148,26 @@ fn main(in: FragmentInput) -> @location(0) vec4 { template <> struct ShaderSource { static constexpr const char* name = "FillExtrusionPatternShader"; - static const std::array attributes; + static const std::array attributes; static constexpr std::array instanceAttributes{}; static const std::array textures; static constexpr auto vertex = R"( struct VertexInput { @location(3) position: vec2, - @location(4) normal_ed: vec4, + @location(4) decimals_ed: vec2, + @location(5) normal2d: vec2, #ifndef HAS_UNIFORM_u_base - @location(5) base: vec2, + @location(6) base: vec2, #endif #ifndef HAS_UNIFORM_u_height - @location(6) height: vec2, + @location(7) height: vec2, #endif #ifndef HAS_UNIFORM_u_pattern_from - @location(7) pattern_from: vec4, + @location(8) pattern_from: vec4, #endif #ifndef HAS_UNIFORM_u_pattern_to - @location(8) pattern_to: vec4, + @location(9) pattern_to: vec4, #endif }; @@ -250,16 +253,18 @@ fn main(in: VertexInput) -> VertexOutput { let heightValue = max(props.height, 0.0); #endif - let normal_i = vec3(f32(in.normal_ed.x), f32(in.normal_ed.y), f32(in.normal_ed.z)); - let edgedistance = f32(in.normal_ed.w); - let t = glMod(normal_i.x, 2.0); - let z = select(baseValue, heightValue, t != 0.0); + let normal = vec3(vec2(in.normal2d) / 16384.0, select(0.0, 1.0, in.normal2d.x == 0 && in.normal2d.y == 0)); + let edgedistance = f32(in.decimals_ed.y); - out.position = drawable.matrix * vec4(f32(in.position.x), f32(in.position.y), z, 1.0); + let t = glMod(f32(in.decimals_ed.x), 2.0); + let z = select(baseValue, heightValue, t > 0.0); + let decimals = unpack_float(f32(in.decimals_ed.x / 2)) / 128.0; + + out.position = drawable.matrix * vec4(vec2(in.position) + decimals, z, 1.0); var patternPos: vec2; - if (normal_i.x == 1.0 && normal_i.y == 0.0 && normal_i.z == 16384.0) { - patternPos = vec2(f32(in.position.x), f32(in.position.y)); + if (normal.z == 1.0) { + patternPos = vec2(in.position); } else { patternPos = vec2(edgedistance, z * drawable.height_factor); } @@ -294,10 +299,10 @@ fn main(in: VertexInput) -> VertexOutput { (pattern_br_b.y - pattern_tl_b.y) / pixelRatio); var lighting = vec4(0.0, 0.0, 0.0, 1.0); - var directional = clamp(dot(normal_i / 16383.0, props.light_position_base.xyz), 0.0, 1.0); + var directional = clamp(dot(normal, props.light_position_base.xyz), 0.0, 1.0); directional = mix(1.0 - props.light_intensity, max(0.5 + props.light_intensity, 1.0), directional); - if (normal_i.y != 0.0) { + if (normal.z == 0.0) { let gradientMin = mix(0.7, 0.98, 1.0 - props.light_intensity); let factor = clamp((t + baseValue) * pow(heightValue / 150.0, 0.5), gradientMin, 1.0); directional *= (1.0 - props.vertical_gradient) + props.vertical_gradient * factor; diff --git a/include/mbgl/style/layers/fill_extrusion_layer.hpp b/include/mbgl/style/layers/fill_extrusion_layer.hpp index ed1bbffd2..08fce59c9 100644 --- a/include/mbgl/style/layers/fill_extrusion_layer.hpp +++ b/include/mbgl/style/layers/fill_extrusion_layer.hpp @@ -19,6 +19,12 @@ public: FillExtrusionLayer(const std::string& layerID, const std::string& sourceID); ~FillExtrusionLayer() override; + // Layout properties + + static PropertyValue getDefaultFillExtrusionRoundedCornerDistance(); + const PropertyValue& getFillExtrusionRoundedCornerDistance() const; + void setFillExtrusionRoundedCornerDistance(const PropertyValue&); + // Paint properties static PropertyValue getDefaultFillExtrusionBase(); diff --git a/platform/android/MapLibreAndroid/src/cpp/style/layers/fill_extrusion_layer.cpp b/platform/android/MapLibreAndroid/src/cpp/style/layers/fill_extrusion_layer.cpp index 75b52dd1c..471862cb9 100644 --- a/platform/android/MapLibreAndroid/src/cpp/style/layers/fill_extrusion_layer.cpp +++ b/platform/android/MapLibreAndroid/src/cpp/style/layers/fill_extrusion_layer.cpp @@ -39,6 +39,17 @@ FillExtrusionLayer::~FillExtrusionLayer() = default; // Property getters +jni::Local> FillExtrusionLayer::getFillExtrusionRoundedCornerDistance(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + auto layer = layerPtr.get(); + if (!layer) { + return std::move(*convert>>( + env, style::FillExtrusionLayer::getDefaultFillExtrusionRoundedCornerDistance())); + } + return std::move( + *convert>>(env, toFillExtrusionLayer(*layer).getFillExtrusionRoundedCornerDistance())); +} + jni::Local> FillExtrusionLayer::getFillExtrusionOpacity(jni::JNIEnv& env) { using namespace mbgl::android::conversion; auto layer = layerPtr.get(); @@ -288,6 +299,8 @@ void FillExtrusionJavaLayerPeerFactory::registerNative(jni::JNIEnv& env) { jni::MakePeer, "initialize", "finalize", + METHOD(&FillExtrusionLayer::getFillExtrusionRoundedCornerDistance, + "nativeGetFillExtrusionRoundedCornerDistance"), METHOD(&FillExtrusionLayer::getFillExtrusionOpacityTransition, "nativeGetFillExtrusionOpacityTransition"), METHOD(&FillExtrusionLayer::setFillExtrusionOpacityTransition, "nativeSetFillExtrusionOpacityTransition"), METHOD(&FillExtrusionLayer::getFillExtrusionOpacity, "nativeGetFillExtrusionOpacity"), diff --git a/platform/android/MapLibreAndroid/src/cpp/style/layers/fill_extrusion_layer.hpp b/platform/android/MapLibreAndroid/src/cpp/style/layers/fill_extrusion_layer.hpp index 1b3055227..2266e0cfc 100644 --- a/platform/android/MapLibreAndroid/src/cpp/style/layers/fill_extrusion_layer.hpp +++ b/platform/android/MapLibreAndroid/src/cpp/style/layers/fill_extrusion_layer.hpp @@ -26,6 +26,8 @@ public: // Properties + jni::Local> getFillExtrusionRoundedCornerDistance(jni::JNIEnv&); + jni::Local> getFillExtrusionOpacity(jni::JNIEnv&); void setFillExtrusionOpacityTransition(jni::JNIEnv&, jlong duration, jlong delay); jni::Local> getFillExtrusionOpacityTransition(jni::JNIEnv&); diff --git a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/style/layers/FillExtrusionLayer.java b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/style/layers/FillExtrusionLayer.java index 4c63fecf5..b6f761a23 100644 --- a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/style/layers/FillExtrusionLayer.java +++ b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/style/layers/FillExtrusionLayer.java @@ -143,6 +143,18 @@ public class FillExtrusionLayer extends Layer { // Property getters + /** + * Get the FillExtrusionRoundedCornerDistance property + * + * @return property wrapper value around Float + */ + @NonNull + @SuppressWarnings("unchecked") + public PropertyValue getFillExtrusionRoundedCornerDistance() { + checkThread(); + return (PropertyValue) new PropertyValue("fill-extrusion-rounded-corner-distance", nativeGetFillExtrusionRoundedCornerDistance()); + } + /** * Get the FillExtrusionOpacity property * @@ -382,6 +394,10 @@ public class FillExtrusionLayer extends Layer { return (PropertyValue) new PropertyValue("fill-extrusion-vertical-gradient", nativeGetFillExtrusionVerticalGradient()); } + @NonNull + @Keep + private native Object nativeGetFillExtrusionRoundedCornerDistance(); + @NonNull @Keep private native Object nativeGetFillExtrusionOpacity(); diff --git a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/style/layers/PropertyFactory.java b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/style/layers/PropertyFactory.java index 82e810313..1c1525f75 100644 --- a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/style/layers/PropertyFactory.java +++ b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/style/layers/PropertyFactory.java @@ -2767,4 +2767,24 @@ The unit is in density-independent pixels only for SDF sprites that were created return new LayoutPropertyValue<>("circle-sort-key", value); } + /** + * The distance from the corner that will be cut and replaced with rounded corner. + * + * @param value a Float value + * @return property wrapper around Float + */ + public static PropertyValue fillExtrusionRoundedCornerDistance(Float value) { + return new LayoutPropertyValue<>("fill-extrusion-rounded-corner-distance", value); + } + + /** + * The distance from the corner that will be cut and replaced with rounded corner. + * + * @param value a Float value + * @return property wrapper around Float + */ + public static PropertyValue fillExtrusionRoundedCornerDistance(Expression value) { + return new LayoutPropertyValue<>("fill-extrusion-rounded-corner-distance", value); + } + } diff --git a/platform/android/MapLibreAndroidTestApp/src/androidTest/java/org/maplibre/android/testapp/style/FillExtrusionLayerTest.java b/platform/android/MapLibreAndroidTestApp/src/androidTest/java/org/maplibre/android/testapp/style/FillExtrusionLayerTest.java index 84a2ebb2a..20c3ae2b3 100644 --- a/platform/android/MapLibreAndroidTestApp/src/androidTest/java/org/maplibre/android/testapp/style/FillExtrusionLayerTest.java +++ b/platform/android/MapLibreAndroidTestApp/src/androidTest/java/org/maplibre/android/testapp/style/FillExtrusionLayerTest.java @@ -175,6 +175,19 @@ public class FillExtrusionLayerTest extends BaseLayerTest { } + @Test + @UiThreadTest + public void testFillExtrusionRoundedCornerDistanceAsConstant() { + Timber.i("fill-extrusion-rounded-corner-distance"); + assertNotNull(layer); + assertNull(layer.getFillExtrusionRoundedCornerDistance().getValue()); + + // Set and Get + Float propertyValue = 0.3f; + layer.setProperties(fillExtrusionRoundedCornerDistance(propertyValue)); + assertEquals(layer.getFillExtrusionRoundedCornerDistance().getValue(), propertyValue); + } + @Test @UiThreadTest public void testFillExtrusionOpacityTransition() { diff --git a/scripts/style-spec-reference/v8.json b/scripts/style-spec-reference/v8.json index 6fd642d53..ad9f8add2 100644 --- a/scripts/style-spec-reference/v8.json +++ b/scripts/style-spec-reference/v8.json @@ -1050,6 +1050,20 @@ } }, "layout_fill-extrusion": { + "fill-extrusion-rounded-corner-distance": { + "type": "number", + "default": 0, + "minimum": 0, + "units": "meters", + "doc": "The distance from the corner that will be cut and replaced with rounded corner.", + "sdk-support": { + "basic functionality": { + "android": "13.0.0", + "ios": "6.0.0" + } + }, + "property-type": "constant" + }, "visibility": { "type": "enum", "values": { diff --git a/shaders/fill_extrusion.vertex.glsl b/shaders/fill_extrusion.vertex.glsl index ae4dc60fd..9182b17b6 100644 --- a/shaders/fill_extrusion.vertex.glsl +++ b/shaders/fill_extrusion.vertex.glsl @@ -1,5 +1,6 @@ layout (location = 0) in vec2 a_pos; -layout (location = 1) in vec4 a_normal_ed; +layout (location = 1) in vec2 a_decimals_ed; +layout (location = 2) in vec2 a_normal2d; out vec4 v_color; layout (std140) uniform FillExtrusionDrawableUBO { @@ -50,14 +51,16 @@ void main() { #pragma mapbox: initialize highp float height #pragma mapbox: initialize highp vec4 color - vec3 normal = a_normal_ed.xyz; + vec3 normal = vec3(a_normal2d / 16384.0, a_normal2d.x == 0.0 && a_normal2d.y == 0.0 ? 1.0 : 0.0); base = max(0.0, base); height = max(0.0, height); - float t = mod(normal.x, 2.0); + float t = mod(a_decimals_ed.x, 2.0); + float z = t > 0.0 ? height : base; + vec2 decimals = unpack_float(floor(a_decimals_ed.x / 2.0)) / 128.0; - gl_Position = u_matrix * vec4(a_pos, t > 0.0 ? height : base, 1); + gl_Position = u_matrix * vec4(a_pos + decimals, z, 1); // Relative luminance (how dark/bright is the surface color?) float colorvalue = color.r * 0.2126 + color.g * 0.7152 + color.b * 0.0722; @@ -69,7 +72,7 @@ void main() { color += ambientlight; // Calculate cos(theta), where theta is the angle between surface normal and diffuse light ray - float directional = clamp(dot(normal / 16384.0, u_lightpos), 0.0, 1.0); + float directional = clamp(dot(normal, u_lightpos), 0.0, 1.0); // Adjust directional so that // the range of values for highlight/shading is narrower @@ -78,12 +81,12 @@ void main() { directional = mix((1.0 - u_lightintensity), max((1.0 - colorvalue + u_lightintensity), 1.0), directional); // Add gradient along z axis of side surfaces - if (normal.y != 0.0) { + if (normal.z == 0.0) { // This avoids another branching statement, but multiplies by a constant of 0.84 if no vertical gradient, // and otherwise calculates the gradient based on base + height - directional *= ( - (1.0 - u_vertical_gradient) + - (u_vertical_gradient * clamp((t + base) * pow(height / 150.0, 0.5), mix(0.7, 0.98, 1.0 - u_lightintensity), 1.0))); + float fMin = mix(0.7, 0.98, 1.0 - u_lightintensity); + float factor = clamp((t + base) * pow(height / 150.0, 0.5), fMin, 1.0); + directional *= (1.0 - u_vertical_gradient) + (u_vertical_gradient * factor); } // Assign final color based on surface + ambient light color, diffuse light directional, and light color diff --git a/shaders/fill_extrusion_pattern.vertex.glsl b/shaders/fill_extrusion_pattern.vertex.glsl index 71645f7f3..3fb057de9 100644 --- a/shaders/fill_extrusion_pattern.vertex.glsl +++ b/shaders/fill_extrusion_pattern.vertex.glsl @@ -1,5 +1,6 @@ layout (location = 0) in vec2 a_pos; -layout (location = 1) in vec4 a_normal_ed; +layout (location = 1) in vec2 a_decimals_ed; +layout (location = 2) in vec2 a_normal2d; out vec2 v_pos_a; out vec2 v_pos_b; @@ -77,8 +78,8 @@ void main() { float fromScale = u_from_scale; float toScale = u_to_scale; - vec3 normal = a_normal_ed.xyz; - float edgedistance = a_normal_ed.w; + vec3 normal = vec3(a_normal2d / 16384.0, a_normal2d.x == 0.0 && a_normal2d.y == 0.0 ? 1.0 : 0.0); + float edgedistance = a_decimals_ed.y; vec2 display_size_a = vec2((pattern_br_a.x - pattern_tl_a.x) / pixelRatio, (pattern_br_a.y - pattern_tl_a.y) / pixelRatio); vec2 display_size_b = vec2((pattern_br_b.x - pattern_tl_b.x) / pixelRatio, (pattern_br_b.y - pattern_tl_b.y) / pixelRatio); @@ -86,28 +87,28 @@ void main() { base = max(0.0, base); height = max(0.0, height); - float t = mod(normal.x, 2.0); + float t = mod(a_decimals_ed.x, 2.0); float z = t > 0.0 ? height : base; + vec2 decimals = unpack_float(floor(a_decimals_ed.x / 2.0)) / 128.0; - gl_Position = u_matrix * vec4(a_pos, z, 1); + gl_Position = u_matrix * vec4(a_pos + decimals, z, 1); - vec2 pos = normal.x == 1.0 && normal.y == 0.0 && normal.z == 16384.0 - ? a_pos // extrusion top - : vec2(edgedistance, z * u_height_factor); // extrusion side + // extrusion top or side + vec2 pos = normal.z == 1.0 ? a_pos : vec2(edgedistance, z * u_height_factor); v_pos_a = get_pattern_pos(u_pixel_coord_upper, u_pixel_coord_lower, fromScale * display_size_a, tileRatio, pos); v_pos_b = get_pattern_pos(u_pixel_coord_upper, u_pixel_coord_lower, toScale * display_size_b, tileRatio, pos); v_lighting = vec4(0.0, 0.0, 0.0, 1.0); - float directional = clamp(dot(normal / 16383.0, u_lightpos), 0.0, 1.0); + float directional = clamp(dot(normal, u_lightpos), 0.0, 1.0); directional = mix((1.0 - u_lightintensity), max((0.5 + u_lightintensity), 1.0), directional); - if (normal.y != 0.0) { + if (normal.z == 0.0) { // This avoids another branching statement, but multiplies by a constant of 0.84 if no vertical gradient, // and otherwise calculates the gradient based on base + height - directional *= ( - (1.0 - u_vertical_gradient) + - (u_vertical_gradient * clamp((t + base) * pow(height / 150.0, 0.5), mix(0.7, 0.98, 1.0 - u_lightintensity), 1.0))); + float fMin = mix(0.7, 0.98, 1.0 - u_lightintensity); + float factor = clamp((t + base) * pow(height / 150.0, 0.5), fMin, 1.0); + directional *= (1.0 - u_vertical_gradient) + (u_vertical_gradient * factor); } v_lighting.rgb += clamp(directional * u_lightcolor, mix(vec3(0.0), vec3(0.3), 1.0 - u_lightcolor), vec3(1.0)); diff --git a/src/mbgl/layermanager/fill_extrusion_layer_factory.cpp b/src/mbgl/layermanager/fill_extrusion_layer_factory.cpp index b9e8f3f68..82b1519d2 100644 --- a/src/mbgl/layermanager/fill_extrusion_layer_factory.cpp +++ b/src/mbgl/layermanager/fill_extrusion_layer_factory.cpp @@ -22,7 +22,10 @@ std::unique_ptr FillExtrusionLayerFactory::createLayout( std::unique_ptr layer, const std::vector>& group) { using namespace style; - using LayoutType = PatternLayout; + using LayoutType = PatternLayout; return std::unique_ptr(new (std::nothrow) LayoutType(parameters.bucketParameters, group, std::move(layer), parameters)); } diff --git a/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp b/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp index fba8d876e..d31633b13 100644 --- a/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp +++ b/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp @@ -6,6 +6,8 @@ #include #include +#include + #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4244) @@ -30,6 +32,16 @@ template <> struct nth<1, mbgl::GeometryCoordinate> { static int64_t get(const mbgl::GeometryCoordinate& t) { return t.y; }; }; + +template <> +struct nth<0, mbgl::GeometryCoordinateFloat> { + static float get(const mbgl::GeometryCoordinateFloat& t) { return t.x; }; +}; + +template <> +struct nth<1, mbgl::GeometryCoordinateFloat> { + static float get(const mbgl::GeometryCoordinateFloat& t) { return t.y; }; +}; } // namespace util } // namespace mapbox @@ -40,10 +52,11 @@ using namespace style; struct GeometryTooLongException : std::exception {}; FillExtrusionBucket::FillExtrusionBucket( - const FillExtrusionBucket::PossiblyEvaluatedLayoutProperties&, + const FillExtrusionBucket::PossiblyEvaluatedLayoutProperties& layout_, const std::map>& layerPaintProperties, const float zoom, - const uint32_t) { + const uint32_t) + : layout(std::move(layout_)) { for (const auto& pair : layerPaintProperties) { paintPropertyBinders.emplace( std::piecewise_construct, @@ -66,12 +79,22 @@ void FillExtrusionBucket::addFeature(const GeometryTileFeature& feature, // Optimize polygons with many interior rings for earcut tessellation. limitHoles(polygon, 500); + std::variant polyVariant = polygon; + double roundedCornerDistance = layout.get(); + if (roundedCornerDistance > 0) { + polyVariant = roundPolygonCorners(polygon, roundedCornerDistance); + } + std::size_t totalVertices = 0; - for (const auto& ring : polygon) { - totalVertices += ring.size(); - if (totalVertices > std::numeric_limits::max()) throw GeometryTooLongException(); - } + std::visit( + [&totalVertices](const auto& poly) { + for (const auto& ring : poly) { + totalVertices += ring.size(); + if (totalVertices > std::numeric_limits::max()) throw GeometryTooLongException(); + } + }, + polyVariant); if (totalVertices == 0) continue; @@ -91,28 +114,15 @@ void FillExtrusionBucket::addFeature(const GeometryTileFeature& feature, assert(triangleIndex + (5 * (totalVertices - 1) + 1) <= std::numeric_limits::max()); - for (const auto& ring : polygon) { - std::size_t nVertices = ring.size(); - - if (nVertices == 0) continue; - - std::size_t edgeDistance = 0; - - for (std::size_t i = 0; i < nVertices; i++) { - const auto& p1 = ring[i]; - + const auto processRingPoints = + [&](const Point& p1, const std::optional>& p2, std::size_t& edgeDistance) { #if MLN_USE_FILL_EXTRUSION_INSTANCING - vertices.emplace_back(layoutVertex(p1, edgeDistance, i == nVertices - 1)); + vertices.emplace_back(layoutVertex(p1, edgeDistance, !p2)); flatIndices.emplace_back(triangleIndex); triangleIndex++; - if (i < nVertices - 1) { - const auto& p2 = ring[i + 1]; - - const auto d1 = convertPoint(p1); - const auto d2 = convertPoint(p2); - - const size_t dist = util::dist(d1, d2); + if (p2) { + const size_t dist = util::dist(p1, *p2); if (edgeDistance + dist > static_cast(std::numeric_limits::max())) { edgeDistance = 0; } @@ -120,34 +130,25 @@ void FillExtrusionBucket::addFeature(const GeometryTileFeature& feature, edgeDistance += dist; } #else - vertices.emplace_back( - FillExtrusionBucket::layoutVertex(p1, 0, 0, 1, 1, static_cast(edgeDistance))); + vertices.emplace_back(FillExtrusionBucket::layoutVertex(p1, 0, 0, 1, edgeDistance)); flatIndices.emplace_back(triangleIndex); triangleIndex++; - if (i != 0) { - const auto& p2 = ring[i - 1]; + if (p2) { + const Point perp = util::unit(util::perp(*p2 - p1)); - const auto d1 = convertPoint(p1); - const auto d2 = convertPoint(p2); - - const Point perp = util::unit(util::perp(d1 - d2)); - const size_t dist = util::dist(d1, d2); - if (edgeDistance + dist > static_cast(std::numeric_limits::max())) { + const size_t dist = util::dist(p1, *p2); + if (edgeDistance + dist > static_cast(std::numeric_limits::max())) { edgeDistance = 0; } - vertices.emplace_back(FillExtrusionBucket::layoutVertex( - p1, perp.x, perp.y, 0, 0, static_cast(edgeDistance))); - vertices.emplace_back(FillExtrusionBucket::layoutVertex( - p1, perp.x, perp.y, 0, 1, static_cast(edgeDistance))); + vertices.emplace_back(FillExtrusionBucket::layoutVertex(*p2, perp.x, perp.y, 0, edgeDistance)); + vertices.emplace_back(FillExtrusionBucket::layoutVertex(*p2, perp.x, perp.y, 1, edgeDistance)); edgeDistance += dist; - vertices.emplace_back(FillExtrusionBucket::layoutVertex( - p2, perp.x, perp.y, 0, 0, static_cast(edgeDistance))); - vertices.emplace_back(FillExtrusionBucket::layoutVertex( - p2, perp.x, perp.y, 0, 1, static_cast(edgeDistance))); + vertices.emplace_back(FillExtrusionBucket::layoutVertex(p1, perp.x, perp.y, 0, edgeDistance)); + vertices.emplace_back(FillExtrusionBucket::layoutVertex(p1, perp.x, perp.y, 1, edgeDistance)); // ┌──────┐ // │ 0 1 │ Counter-Clockwise winding order. @@ -161,10 +162,32 @@ void FillExtrusionBucket::addFeature(const GeometryTileFeature& feature, triangleSegment.indexLength += 6; } #endif - } - } + }; - std::vector indices = mapbox::earcut(polygon); + std::vector indices; + std::visit( + [&indices, processRingPoints](const auto& poly) { + for (const auto& ring : poly) { + std::size_t nVertices = ring.size(); + + if (nVertices == 0) continue; + + std::size_t edgeDistance = 0; + + for (std::size_t i = 0; i < nVertices; i++) { + const auto p1 = convertPoint(ring[i]); + + std::optional> p2 = std::nullopt; + if (i < nVertices - 1) { + p2 = convertPoint(ring[i + 1]); + } + processRingPoints(p1, p2, edgeDistance); + } + } + + indices = mapbox::earcut(poly); + }, + polyVariant); std::size_t nIndices = indices.size(); assert(nIndices % 3 == 0); diff --git a/src/mbgl/renderer/buckets/fill_extrusion_bucket.hpp b/src/mbgl/renderer/buckets/fill_extrusion_bucket.hpp index e270c9fa2..52391a662 100644 --- a/src/mbgl/renderer/buckets/fill_extrusion_bucket.hpp +++ b/src/mbgl/renderer/buckets/fill_extrusion_bucket.hpp @@ -18,18 +18,18 @@ using FillExtrusionBinders = PaintPropertyBinders>; #if MLN_USE_FILL_EXTRUSION_INSTANCING -using FillExtrusionLayoutVertex = gfx::Vertex>; +using FillExtrusionLayoutVertex = gfx::Vertex>; #else -using FillExtrusionLayoutVertex = gfx::Vertex>; +using FillExtrusionLayoutVertex = gfx::Vertex>; #endif class FillExtrusionBucket final : public Bucket { public: ~FillExtrusionBucket() override; using PossiblyEvaluatedPaintProperties = style::FillExtrusionPaintProperties::PossiblyEvaluated; - using PossiblyEvaluatedLayoutProperties = style::Properties<>::PossiblyEvaluated; + using PossiblyEvaluatedLayoutProperties = style::FillExtrusionLayoutProperties::PossiblyEvaluated; - FillExtrusionBucket(const PossiblyEvaluatedLayoutProperties&, + FillExtrusionBucket(const PossiblyEvaluatedLayoutProperties& layout, const std::map>&, float, uint32_t); @@ -50,31 +50,40 @@ public: void update(const FeatureStates&, const GeometryTileLayer&, const std::string&, const ImagePositions&) override; #if MLN_USE_FILL_EXTRUSION_INSTANCING - static FillExtrusionLayoutVertex layoutVertex(Point p, uint16_t edgeDistance, bool isDiscarded) { - return FillExtrusionLayoutVertex{{p.x, p.y}, - { // The edgeDistance attribute is used for wrapping fill_extrusion patterns - edgeDistance, - // When used as instance vector this specify if an instance is discarded - static_cast(isDiscarded) - }}; + static FillExtrusionLayoutVertex layoutVertex(const Point& p, uint16_t edgeDistance, bool isDiscarded) { + auto intPart = Point(std::floor(p.x), std::floor(p.y)); + // Multiply factional part by 2^7 to pack them into integers [0..127] + auto fracPart = convertPoint((p - intPart) * 128.0); + + return FillExtrusionLayoutVertex{ + {static_cast(intPart.x), static_cast(intPart.y)}, + { // We pack a bool (`isDiscarded`) indicating whether this instance is discarded + static_cast((fracPart.x * 256 + fracPart.y) * 2 + (isDiscarded ? 1 : 0)), + // The edgeDistance attribute is used for wrapping fill_extrusion patterns + edgeDistance + }}; } #else static FillExtrusionLayoutVertex layoutVertex( - Point p, double nx, double ny, double nz, unsigned short t, uint16_t e) { - const auto factor = pow(2, 13); + const Point& p, double nx, double ny, unsigned short t, uint16_t edgeDistance) { + const auto factor = pow(2, 14); + auto intPart = Point(std::floor(p.x), std::floor(p.y)); + // Multiply factional part by 2^7 to pack them into integers [0..127] + auto fracPart = convertPoint((p - intPart) * 128.0); - return FillExtrusionLayoutVertex{{{p.x, p.y}}, - {{// Multiply normal vector components by 2^13 to pack them into - // integers We pack a bool (`t`) into the x component indicating - // whether it is an upper or lower vertex - static_cast(floor(nx * factor) * 2 + t), - static_cast(ny * factor * 2), - static_cast(nz * factor * 2), - // The edgedistance attribute is used for wrapping fill_extrusion patterns - static_cast(e)}}}; + return FillExtrusionLayoutVertex{ + {static_cast(intPart.x), static_cast(intPart.y)}, + {// We pack a bool (`t`) indicating whether it is an upper or lower vertex + static_cast((fracPart.x * 256 + fracPart.y) * 2 + t), + // The edgedistance attribute is used for wrapping fill_extrusion patterns + edgeDistance}, + // Multiply normal vector components in the 2D plane by 2^14 to pack them into integers + {static_cast(nx * factor), static_cast(ny * factor)}}; } #endif + PossiblyEvaluatedLayoutProperties layout; + static std::array lightColor(const EvaluatedLight&); static std::array lightPosition(const EvaluatedLight&, const TransformState&); static float lightIntensity(const EvaluatedLight&); diff --git a/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp index ba0678b38..f941af8cc 100644 --- a/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp +++ b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp @@ -346,13 +346,22 @@ void RenderFillExtrusionLayer::update(gfx::ShaderRegistry& shaders, sizeof(FillExtrusionLayoutVertex), gfx::AttributeDataType::Short2); } -#if !MLN_USE_FILL_EXTRUSION_INSTANCING - if (const auto& attr = vertexAttrs->set(idFillExtrusionNormalEdVertexAttribute)) { + + if (const auto& attr = vertexAttrs->set(idFillExtrusionDecimalsEdAttribute)) { attr->setSharedRawData(bucket.sharedVertices, offsetof(FillExtrusionLayoutVertex, a2), /*vertexOffset=*/0, sizeof(FillExtrusionLayoutVertex), - gfx::AttributeDataType::Short4); + gfx::AttributeDataType::UShort2); + } + +#if !MLN_USE_FILL_EXTRUSION_INSTANCING + if (const auto& attr = vertexAttrs->set(idFillExtrusionNormal2DVertexAttribute)) { + attr->setSharedRawData(bucket.sharedVertices, + offsetof(FillExtrusionLayoutVertex, a3), + /*vertexOffset=*/0, + sizeof(FillExtrusionLayoutVertex), + gfx::AttributeDataType::Short2); } #endif @@ -438,7 +447,7 @@ void RenderFillExtrusionLayer::update(gfx::ShaderRegistry& shaders, sizeof(FillExtrusionLayoutVertex), gfx::AttributeDataType::Short2); } - if (const auto& attr = instanceAttrs->set(idFillExtrusionEdDiscardAttribute)) { + if (const auto& attr = instanceAttrs->set(idFillExtrusionDecimalsEdAttribute)) { attr->setSharedRawData(bucket.sharedVertices, offsetof(FillExtrusionLayoutVertex, a2), /*vertexOffset=*/0, diff --git a/src/mbgl/shaders/attributes.hpp b/src/mbgl/shaders/attributes.hpp index 976a2a375..9992bf9c4 100644 --- a/src/mbgl/shaders/attributes.hpp +++ b/src/mbgl/shaders/attributes.hpp @@ -22,12 +22,8 @@ MBGL_DEFINE_ATTRIBUTE(uint16_t, 2, placed); MBGL_DEFINE_ATTRIBUTE(uint16_t, 3, size); MBGL_DEFINE_ATTRIBUTE(float, 1, offset); MBGL_DEFINE_ATTRIBUTE(float, 2, shift); - -#if MLN_USE_FILL_EXTRUSION_INSTANCING -MBGL_DEFINE_ATTRIBUTE(uint16_t, 2, ed_discard); -#else -MBGL_DEFINE_ATTRIBUTE(int16_t, 4, normal_ed); -#endif +MBGL_DEFINE_ATTRIBUTE(uint16_t, 2, decimals_ed); +MBGL_DEFINE_ATTRIBUTE(int16_t, 2, normal2d); template struct data { diff --git a/src/mbgl/shaders/gl/shader_info.cpp b/src/mbgl/shaders/gl/shader_info.cpp index 8a4df03d2..2c98c4d96 100644 --- a/src/mbgl/shaders/gl/shader_info.cpp +++ b/src/mbgl/shaders/gl/shader_info.cpp @@ -228,7 +228,8 @@ const std::vector FillExtrusionShaderInfo::uniformBlocks = { }; const std::vector FillExtrusionShaderInfo::attributes = { AttributeInfo{"a_pos", idFillExtrusionPosVertexAttribute}, - AttributeInfo{"a_normal_ed", idFillExtrusionNormalEdVertexAttribute}, + AttributeInfo{"a_decimals_ed", idFillExtrusionDecimalsEdAttribute}, + AttributeInfo{"a_normal2d", idFillExtrusionNormal2DVertexAttribute}, AttributeInfo{"a_base", idFillExtrusionBaseVertexAttribute}, AttributeInfo{"a_height", idFillExtrusionHeightVertexAttribute}, AttributeInfo{"a_color", idFillExtrusionColorVertexAttribute}, @@ -246,7 +247,8 @@ const std::vector FillExtrusionPatternShaderInfo::uniformBlock }; const std::vector FillExtrusionPatternShaderInfo::attributes = { AttributeInfo{"a_pos", idFillExtrusionPosVertexAttribute}, - AttributeInfo{"a_normal_ed", idFillExtrusionNormalEdVertexAttribute}, + AttributeInfo{"a_decimals_ed", idFillExtrusionDecimalsEdAttribute}, + AttributeInfo{"a_normal2d", idFillExtrusionNormal2DVertexAttribute}, AttributeInfo{"a_base", idFillExtrusionBaseVertexAttribute}, AttributeInfo{"a_height", idFillExtrusionHeightVertexAttribute}, AttributeInfo{"a_pattern_from", idFillExtrusionPatternFromVertexAttribute}, diff --git a/src/mbgl/shaders/mtl/fill_extrusion.cpp b/src/mbgl/shaders/mtl/fill_extrusion.cpp index 42732dec1..e54f0c100 100644 --- a/src/mbgl/shaders/mtl/fill_extrusion.cpp +++ b/src/mbgl/shaders/mtl/fill_extrusion.cpp @@ -9,13 +9,14 @@ namespace shaders { using FillExtrusionShaderSource = ShaderSource; -const std::array FillExtrusionShaderSource::attributes = { +const std::array FillExtrusionShaderSource::attributes = { AttributeInfo{0, gfx::AttributeDataType::Short2, fillExtrusionUBOCount + 0, idFillExtrusionPosVertexAttribute}, + AttributeInfo{1, gfx::AttributeDataType::UShort2, fillExtrusionUBOCount + 0, idFillExtrusionDecimalsEdAttribute}, // Data driven - AttributeInfo{1, gfx::AttributeDataType::Float4, fillExtrusionUBOCount + 1, idFillExtrusionColorVertexAttribute}, - AttributeInfo{2, gfx::AttributeDataType::Float, fillExtrusionUBOCount + 1, idFillExtrusionBaseVertexAttribute}, - AttributeInfo{3, gfx::AttributeDataType::Float, fillExtrusionUBOCount + 1, idFillExtrusionHeightVertexAttribute}, + AttributeInfo{2, gfx::AttributeDataType::Float4, fillExtrusionUBOCount + 1, idFillExtrusionColorVertexAttribute}, + AttributeInfo{3, gfx::AttributeDataType::Float, fillExtrusionUBOCount + 1, idFillExtrusionBaseVertexAttribute}, + AttributeInfo{4, gfx::AttributeDataType::Float, fillExtrusionUBOCount + 1, idFillExtrusionHeightVertexAttribute}, }; const std::array FillExtrusionShaderSource::textures = {}; @@ -30,7 +31,7 @@ const std::array FillExtrusionInstancedShaderSource::attribute }; const std::array FillExtrusionInstancedShaderSource::instanceAttributes = { AttributeInfo{1, gfx::AttributeDataType::Short2, fillExtrusionUBOCount + 1, idFillExtrusionOutlinePosAttribute}, - AttributeInfo{2, gfx::AttributeDataType::UShort2, fillExtrusionUBOCount + 1, idFillExtrusionEdDiscardAttribute}, + AttributeInfo{2, gfx::AttributeDataType::UShort2, fillExtrusionUBOCount + 1, idFillExtrusionDecimalsEdAttribute}, // Data driven AttributeInfo{3, gfx::AttributeDataType::Float4, fillExtrusionUBOCount + 2, idFillExtrusionColorVertexAttribute}, @@ -44,16 +45,17 @@ const std::array FillExtrusionInstancedShaderSource::textures = using FillExtrusionPatternShaderSource = ShaderSource; -const std::array FillExtrusionPatternShaderSource::attributes = { +const std::array FillExtrusionPatternShaderSource::attributes = { AttributeInfo{0, gfx::AttributeDataType::Short2, fillExtrusionUBOCount + 0, idFillExtrusionPosVertexAttribute}, + AttributeInfo{1, gfx::AttributeDataType::UShort2, fillExtrusionUBOCount + 0, idFillExtrusionDecimalsEdAttribute}, // Data driven - AttributeInfo{1, gfx::AttributeDataType::Float, fillExtrusionUBOCount + 1, idFillExtrusionBaseVertexAttribute}, - AttributeInfo{2, gfx::AttributeDataType::Float, fillExtrusionUBOCount + 1, idFillExtrusionHeightVertexAttribute}, + AttributeInfo{2, gfx::AttributeDataType::Float, fillExtrusionUBOCount + 1, idFillExtrusionBaseVertexAttribute}, + AttributeInfo{3, gfx::AttributeDataType::Float, fillExtrusionUBOCount + 1, idFillExtrusionHeightVertexAttribute}, AttributeInfo{ - 3, gfx::AttributeDataType::UShort4, fillExtrusionUBOCount + 1, idFillExtrusionPatternFromVertexAttribute}, + 4, gfx::AttributeDataType::UShort4, fillExtrusionUBOCount + 1, idFillExtrusionPatternFromVertexAttribute}, AttributeInfo{ - 4, gfx::AttributeDataType::UShort4, fillExtrusionUBOCount + 1, idFillExtrusionPatternToVertexAttribute}, + 5, gfx::AttributeDataType::UShort4, fillExtrusionUBOCount + 1, idFillExtrusionPatternToVertexAttribute}, }; const std::array FillExtrusionPatternShaderSource::textures = { TextureInfo{0, idFillExtrusionImageTexture}, @@ -70,7 +72,7 @@ const std::array FillExtrusionPatternInstancedShaderSource::at }; const std::array FillExtrusionPatternInstancedShaderSource::instanceAttributes = { AttributeInfo{1, gfx::AttributeDataType::Short2, fillExtrusionUBOCount + 1, idFillExtrusionOutlinePosAttribute}, - AttributeInfo{2, gfx::AttributeDataType::UShort2, fillExtrusionUBOCount + 1, idFillExtrusionEdDiscardAttribute}, + AttributeInfo{2, gfx::AttributeDataType::UShort2, fillExtrusionUBOCount + 1, idFillExtrusionDecimalsEdAttribute}, // Data driven AttributeInfo{3, gfx::AttributeDataType::Float, fillExtrusionUBOCount + 2, idFillExtrusionBaseVertexAttribute}, diff --git a/src/mbgl/shaders/vulkan/fill_extrusion.cpp b/src/mbgl/shaders/vulkan/fill_extrusion.cpp index b9f6836e4..f09f0f9f9 100644 --- a/src/mbgl/shaders/vulkan/fill_extrusion.cpp +++ b/src/mbgl/shaders/vulkan/fill_extrusion.cpp @@ -12,11 +12,12 @@ namespace shaders { using FillExtrusionShaderSource = ShaderSource; -const std::array FillExtrusionShaderSource::attributes = { +const std::array FillExtrusionShaderSource::attributes = { AttributeInfo{0, gfx::AttributeDataType::Short2, idFillExtrusionPosVertexAttribute, idFillExtrusionInstanced}, - AttributeInfo{1, gfx::AttributeDataType::Float4, idFillExtrusionColorVertexAttribute}, - AttributeInfo{2, gfx::AttributeDataType::Float, idFillExtrusionBaseVertexAttribute}, - AttributeInfo{3, gfx::AttributeDataType::Float, idFillExtrusionHeightVertexAttribute}, + AttributeInfo{1, gfx::AttributeDataType::UShort2, idFillExtrusionDecimalsEdAttribute, idFillExtrusionInstanced}, + AttributeInfo{2, gfx::AttributeDataType::Float4, idFillExtrusionColorVertexAttribute}, + AttributeInfo{3, gfx::AttributeDataType::Float, idFillExtrusionBaseVertexAttribute}, + AttributeInfo{4, gfx::AttributeDataType::Float, idFillExtrusionHeightVertexAttribute}, }; const std::array FillExtrusionShaderSource::textures = {}; @@ -31,7 +32,7 @@ const std::array FillExtrusionInstancedShaderSource::attribute }; const std::array FillExtrusionInstancedShaderSource::instanceAttributes = { AttributeInfo{1, gfx::AttributeDataType::Short2, idFillExtrusionOutlinePosAttribute, idFillExtrusionInstanced}, - AttributeInfo{2, gfx::AttributeDataType::UShort2, idFillExtrusionEdDiscardAttribute, idFillExtrusionInstanced}, + AttributeInfo{2, gfx::AttributeDataType::UShort2, idFillExtrusionDecimalsEdAttribute, idFillExtrusionInstanced}, // Data driven AttributeInfo{3, gfx::AttributeDataType::Float4, idFillExtrusionColorVertexAttribute}, @@ -45,12 +46,13 @@ const std::array FillExtrusionInstancedShaderSource::textures = using FillExtrusionPatternShaderSource = ShaderSource; -const std::array FillExtrusionPatternShaderSource::attributes = { +const std::array FillExtrusionPatternShaderSource::attributes = { AttributeInfo{0, gfx::AttributeDataType::Short2, idFillExtrusionPosVertexAttribute, idFillExtrusionInstanced}, - AttributeInfo{1, gfx::AttributeDataType::Float, idFillExtrusionBaseVertexAttribute}, - AttributeInfo{2, gfx::AttributeDataType::Float, idFillExtrusionHeightVertexAttribute}, - AttributeInfo{3, gfx::AttributeDataType::UShort4, idFillExtrusionPatternFromVertexAttribute}, - AttributeInfo{4, gfx::AttributeDataType::UShort4, idFillExtrusionPatternToVertexAttribute}, + AttributeInfo{1, gfx::AttributeDataType::UShort2, idFillExtrusionDecimalsEdAttribute, idFillExtrusionInstanced}, + AttributeInfo{2, gfx::AttributeDataType::Float, idFillExtrusionBaseVertexAttribute}, + AttributeInfo{3, gfx::AttributeDataType::Float, idFillExtrusionHeightVertexAttribute}, + AttributeInfo{4, gfx::AttributeDataType::UShort4, idFillExtrusionPatternFromVertexAttribute}, + AttributeInfo{5, gfx::AttributeDataType::UShort4, idFillExtrusionPatternToVertexAttribute}, }; const std::array FillExtrusionPatternShaderSource::textures = { TextureInfo{0, idFillExtrusionImageTexture}, @@ -67,7 +69,7 @@ const std::array FillExtrusionPatternInstancedShaderSource::at }; const std::array FillExtrusionPatternInstancedShaderSource::instanceAttributes = { AttributeInfo{1, gfx::AttributeDataType::Short2, idFillExtrusionOutlinePosAttribute, idFillExtrusionInstanced}, - AttributeInfo{2, gfx::AttributeDataType::UShort2, idFillExtrusionEdDiscardAttribute, idFillExtrusionInstanced}, + AttributeInfo{2, gfx::AttributeDataType::UShort2, idFillExtrusionDecimalsEdAttribute, idFillExtrusionInstanced}, // Data driven AttributeInfo{3, gfx::AttributeDataType::Float, idFillExtrusionBaseVertexAttribute}, diff --git a/src/mbgl/shaders/webgpu/fill_extrusion.cpp b/src/mbgl/shaders/webgpu/fill_extrusion.cpp index 7b156a6bc..14d8d63bb 100644 --- a/src/mbgl/shaders/webgpu/fill_extrusion.cpp +++ b/src/mbgl/shaders/webgpu/fill_extrusion.cpp @@ -10,12 +10,13 @@ namespace shaders { using FillExtrusionShaderSource = ShaderSource; -const std::array FillExtrusionShaderSource::attributes = { +const std::array FillExtrusionShaderSource::attributes = { AttributeInfo{3, gfx::AttributeDataType::Short2, idFillExtrusionPosVertexAttribute}, - AttributeInfo{4, gfx::AttributeDataType::Short4, idFillExtrusionNormalEdVertexAttribute}, - AttributeInfo{5, gfx::AttributeDataType::Float4, idFillExtrusionColorVertexAttribute}, - AttributeInfo{6, gfx::AttributeDataType::Float2, idFillExtrusionBaseVertexAttribute}, - AttributeInfo{7, gfx::AttributeDataType::Float2, idFillExtrusionHeightVertexAttribute}, + AttributeInfo{4, gfx::AttributeDataType::UShort2, idFillExtrusionDecimalsEdAttribute}, + AttributeInfo{5, gfx::AttributeDataType::Short2, idFillExtrusionNormal2DVertexAttribute}, + AttributeInfo{6, gfx::AttributeDataType::Float4, idFillExtrusionColorVertexAttribute}, + AttributeInfo{7, gfx::AttributeDataType::Float2, idFillExtrusionBaseVertexAttribute}, + AttributeInfo{8, gfx::AttributeDataType::Float2, idFillExtrusionHeightVertexAttribute}, }; const std::array FillExtrusionShaderSource::textures = {}; @@ -24,13 +25,14 @@ const std::array FillExtrusionShaderSource::textures = {}; using FillExtrusionPatternShaderSource = ShaderSource; -const std::array FillExtrusionPatternShaderSource::attributes = { +const std::array FillExtrusionPatternShaderSource::attributes = { AttributeInfo{3, gfx::AttributeDataType::Short2, idFillExtrusionPosVertexAttribute}, - AttributeInfo{4, gfx::AttributeDataType::Short4, idFillExtrusionNormalEdVertexAttribute}, - AttributeInfo{5, gfx::AttributeDataType::Float2, idFillExtrusionBaseVertexAttribute}, - AttributeInfo{6, gfx::AttributeDataType::Float2, idFillExtrusionHeightVertexAttribute}, - AttributeInfo{7, gfx::AttributeDataType::UShort4, idFillExtrusionPatternFromVertexAttribute}, - AttributeInfo{8, gfx::AttributeDataType::UShort4, idFillExtrusionPatternToVertexAttribute}, + AttributeInfo{4, gfx::AttributeDataType::UShort2, idFillExtrusionDecimalsEdAttribute}, + AttributeInfo{5, gfx::AttributeDataType::Short2, idFillExtrusionNormal2DVertexAttribute}, + AttributeInfo{6, gfx::AttributeDataType::Float2, idFillExtrusionBaseVertexAttribute}, + AttributeInfo{7, gfx::AttributeDataType::Float2, idFillExtrusionHeightVertexAttribute}, + AttributeInfo{8, gfx::AttributeDataType::UShort4, idFillExtrusionPatternFromVertexAttribute}, + AttributeInfo{9, gfx::AttributeDataType::UShort4, idFillExtrusionPatternToVertexAttribute}, }; const std::array FillExtrusionPatternShaderSource::textures = { TextureInfo{0, idFillExtrusionImageTexture}, diff --git a/src/mbgl/style/layers/fill_extrusion_layer.cpp b/src/mbgl/style/layers/fill_extrusion_layer.cpp index 3f4ff9cbc..f06d6cf4f 100644 --- a/src/mbgl/style/layers/fill_extrusion_layer.cpp +++ b/src/mbgl/style/layers/fill_extrusion_layer.cpp @@ -58,11 +58,27 @@ std::unique_ptr FillExtrusionLayer::cloneRef(const std::string& id_) cons return std::make_unique(std::move(impl_)); } -void FillExtrusionLayer::Impl::stringifyLayout(rapidjson::Writer&) const { +void FillExtrusionLayer::Impl::stringifyLayout(rapidjson::Writer& writer) const { + layout.stringify(writer); } // Layout properties +PropertyValue FillExtrusionLayer::getDefaultFillExtrusionRoundedCornerDistance() { + return FillExtrusionRoundedCornerDistance::defaultValue(); +} + +const PropertyValue& FillExtrusionLayer::getFillExtrusionRoundedCornerDistance() const { + return impl().layout.get(); +} + +void FillExtrusionLayer::setFillExtrusionRoundedCornerDistance(const PropertyValue& value) { + if (value == getFillExtrusionRoundedCornerDistance()) return; + auto impl_ = mutableImpl(); + impl_->layout.get() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} // Paint properties @@ -305,6 +321,7 @@ enum class Property : uint8_t { FillExtrusionTranslateTransition, FillExtrusionTranslateAnchorTransition, FillExtrusionVerticalGradientTransition, + FillExtrusionRoundedCornerDistance = kPaintPropertyCount, }; template @@ -328,7 +345,8 @@ constexpr const auto layerProperties = mapbox::eternal::hash_map FillExtrusionLayer::setPropertyInternal(const std::string& setFillExtrusionColor(*typedValue); return std::nullopt; } - if (property == Property::FillExtrusionOpacity) { + if (property == Property::FillExtrusionOpacity || property == Property::FillExtrusionRoundedCornerDistance) { Error error; const auto& typedValue = convert>(value, error, false, false); if (!typedValue) { return error; } - setFillExtrusionOpacity(*typedValue); - return std::nullopt; + if (property == Property::FillExtrusionOpacity) { + setFillExtrusionOpacity(*typedValue); + return std::nullopt; + } + + if (property == Property::FillExtrusionRoundedCornerDistance) { + setFillExtrusionRoundedCornerDistance(*typedValue); + return std::nullopt; + } } if (property == Property::FillExtrusionPattern) { Error error; diff --git a/src/mbgl/style/layers/fill_extrusion_layer_impl.hpp b/src/mbgl/style/layers/fill_extrusion_layer_impl.hpp index dcb6de1d8..431d5a302 100644 --- a/src/mbgl/style/layers/fill_extrusion_layer_impl.hpp +++ b/src/mbgl/style/layers/fill_extrusion_layer_impl.hpp @@ -14,7 +14,7 @@ public: bool hasLayoutDifference(const Layer::Impl&) const override; void stringifyLayout(rapidjson::Writer&) const override; - Properties<>::Unevaluated layout; + FillExtrusionLayoutProperties::Unevaluated layout; FillExtrusionPaintProperties::Transitionable paint; DECLARE_LAYER_TYPE_INFO; diff --git a/src/mbgl/style/layers/fill_extrusion_layer_properties.cpp b/src/mbgl/style/layers/fill_extrusion_layer_properties.cpp index 6786deb7c..baf60ca06 100644 --- a/src/mbgl/style/layers/fill_extrusion_layer_properties.cpp +++ b/src/mbgl/style/layers/fill_extrusion_layer_properties.cpp @@ -32,7 +32,7 @@ const FillExtrusionLayer::Impl& FillExtrusionLayerProperties::layerImpl() const } expression::Dependency FillExtrusionLayerProperties::getDependencies() const noexcept { - return layerImpl().paint.getDependencies(); + return layerImpl().paint.getDependencies() | layerImpl().layout.getDependencies(); } } // namespace style diff --git a/src/mbgl/style/layers/fill_extrusion_layer_properties.hpp b/src/mbgl/style/layers/fill_extrusion_layer_properties.hpp index 2808b95fd..62a982dd5 100644 --- a/src/mbgl/style/layers/fill_extrusion_layer_properties.hpp +++ b/src/mbgl/style/layers/fill_extrusion_layer_properties.hpp @@ -16,6 +16,11 @@ namespace mbgl { namespace style { +struct FillExtrusionRoundedCornerDistance : LayoutProperty { + static constexpr const char *name() { return "fill-extrusion-rounded-corner-distance"; } + static float defaultValue() { return 0.f; } +}; + struct FillExtrusionBase : DataDrivenPaintProperty { static float defaultValue() { return 0.f; } }; @@ -48,6 +53,10 @@ struct FillExtrusionVerticalGradient : PaintProperty { static bool defaultValue() { return true; } }; +class FillExtrusionLayoutProperties : public Properties< + FillExtrusionRoundedCornerDistance +> {}; + class FillExtrusionPaintProperties : public Properties< FillExtrusionBase, FillExtrusionColor, diff --git a/src/mbgl/tile/geometry_tile_data.cpp b/src/mbgl/tile/geometry_tile_data.cpp index 59e7c0fe4..f713007b7 100644 --- a/src/mbgl/tile/geometry_tile_data.cpp +++ b/src/mbgl/tile/geometry_tile_data.cpp @@ -1,7 +1,9 @@ #include #include +#include #include #include +#include #ifdef _MSC_VER #pragma warning(push) @@ -299,4 +301,74 @@ const GeometryCollection& GeometryTileFeature::getGeometries() const { return dummy; } +GeometryCollectionFloat roundPolygonCorners(GeometryCollection& polygon, double desiredCornerDistance) { + const int arcPoints = 3; + const double maxEdgeLenPercent = 0.2; + const double sinParallelTreshold = sin(util::deg2rad(5)); + GeometryCollectionFloat roundedCornerPolygon; + + for (auto ring : polygon) { + GeometryCoordinatesFloat roundedCornerRing; + + std::size_t nVertices = ring.size() - 1; + for (std::size_t i = 0; i < nVertices; i++) { + auto prevPoint = convertPoint(ring[(i - 1 + nVertices) % nVertices]); + auto cornerPoint = convertPoint(ring[i]); + auto nextPoint = convertPoint(ring[(i + 1) % nVertices]); + + // Compute the start and end points of the rounded corner + auto edge1Vector = util::normal(prevPoint, cornerPoint); + auto edge2Vector = util::normal(cornerPoint, nextPoint); + + auto edge1MaxCornerDistance = util::dist(cornerPoint, prevPoint) * maxEdgeLenPercent; + auto edge2MaxCornerDistance = util::dist(cornerPoint, nextPoint) * maxEdgeLenPercent; + auto cornerDistance = std::min({desiredCornerDistance, edge1MaxCornerDistance, edge2MaxCornerDistance}); + + auto startPoint = cornerPoint - edge1Vector * cornerDistance; + auto endPoint = cornerPoint + edge2Vector * cornerDistance; + + // Perpendicular directions + auto perp1Vector = util::perp(edge1Vector); + auto perp2Vector = util::perp(edge2Vector); + + // Ensure perpendiculars point toward same side + if (util::crossProduct(edge1Vector, edge2Vector) < 0) { + perp1Vector *= -1.0; + perp2Vector *= -1.0; + } + + // Center = intersection of perpendiculars + auto perpCrossProduct = util::crossProduct(perp1Vector, perp2Vector); + if (std::abs(perpCrossProduct) < sinParallelTreshold) { + roundedCornerRing.emplace_back(convertPoint(ring[i])); + continue; + } + auto t = util::crossProduct(endPoint - startPoint, perp2Vector) / perpCrossProduct; + auto centerPoint = startPoint + perp1Vector * t; + + // Add the start point of the rounded corner + roundedCornerRing.emplace_back(convertPoint(startPoint)); + + // Generate points along the arc + auto radius = util::dist(startPoint, centerPoint); + auto startAngle = std::atan2(startPoint.y - centerPoint.y, startPoint.x - centerPoint.x); + auto arcAngle = util::angle_between(startPoint - centerPoint, endPoint - centerPoint); + for (int k = 1; k <= arcPoints; k++) { + double angle = startAngle + arcAngle * k / (arcPoints + 1); + auto arcPoint = Point(centerPoint.x + std::cos(angle) * radius, + centerPoint.y + std::sin(angle) * radius); + roundedCornerRing.emplace_back(convertPoint(arcPoint)); + } + + // Add the end point of the rounded corner + roundedCornerRing.emplace_back(convertPoint(endPoint)); + } + // Close the ring by repeating the first point at the end + roundedCornerRing.emplace_back(roundedCornerRing[0]); + + // Add the ring to the rounded corner polygon + roundedCornerPolygon.emplace_back(roundedCornerRing); + } + return roundedCornerPolygon; +} } // namespace mbgl diff --git a/src/mbgl/tile/geometry_tile_data.hpp b/src/mbgl/tile/geometry_tile_data.hpp index b8f395f4c..d4215a961 100644 --- a/src/mbgl/tile/geometry_tile_data.hpp +++ b/src/mbgl/tile/geometry_tile_data.hpp @@ -145,4 +145,10 @@ struct ToGeometryCollection { } }; +using GeometryCoordinateFloat = Point; +using GeometryCoordinatesFloat = std::vector; +using GeometryCollectionFloat = std::vector; + +GeometryCollectionFloat roundPolygonCorners(GeometryCollection& polygon, double radius); + } // namespace mbgl diff --git a/src/mbgl/util/math.hpp b/src/mbgl/util/math.hpp index 8d5efdceb..c3383cdcc 100644 --- a/src/mbgl/util/math.hpp +++ b/src/mbgl/util/math.hpp @@ -12,6 +12,16 @@ namespace util { // TODO: split this file up into individual headers, following mbgl/math/*.hpp. +template +T dotProduct(const Point& a, const Point& b) { + return a.x * b.x + a.y * b.y; +} + +template +T crossProduct(const Point& a, const Point& b) { + return a.x * b.y - a.y * b.x; +} + // Find the angle of the two vectors, solving the formula for the cross product // a x b = |a||b|sin(θ) for θ. template