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 <bart@emeel.net>
This commit is contained in:
Alex Cristici
2026-07-21 17:56:36 +03:00
committed by GitHub
parent 7426c42e64
commit d866b289cc
31 changed files with 579 additions and 291 deletions

View File

@@ -9,7 +9,8 @@ template <>
struct ShaderSource<BuiltIn::FillExtrusionShader, gfx::Backend::Type::OpenGL> {
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

View File

@@ -9,7 +9,8 @@ template <>
struct ShaderSource<BuiltIn::FillExtrusionPatternShader, gfx::Backend::Type::OpenGL> {
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));

View File

@@ -69,7 +69,7 @@ struct ShaderSource<BuiltIn::FillExtrusionShader, gfx::Backend::Type::Metal> {
static constexpr auto vertexMainFunction = "vertexMain";
static constexpr auto fragmentMainFunction = "fragmentMain";
static const std::array<AttributeInfo, 4> attributes;
static const std::array<AttributeInfo, 5> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 0> textures;
@@ -78,15 +78,16 @@ struct ShaderSource<BuiltIn::FillExtrusionShader, gfx::Backend::Type::Metal> {
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<BuiltIn::FillExtrusionPatternShader, gfx::Backend::Type::Met
static constexpr auto vertexMainFunction = "vertexMain";
static constexpr auto fragmentMainFunction = "fragmentMain";
static const std::array<AttributeInfo, 5> attributes;
static const std::array<AttributeInfo, 6> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 1> textures;
@@ -340,18 +341,19 @@ struct ShaderSource<BuiltIn::FillExtrusionPatternShader, gfx::Backend::Type::Met
struct VertexStage {
short2 pos [[attribute(0)]];
ushort2 decimals_ed [[attribute(1)]];
#if !defined(HAS_UNIFORM_u_base)
float base [[attribute(1)]];
float base [[attribute(2)]];
#endif
#if !defined(HAS_UNIFORM_u_height)
float height [[attribute(2)]];
float height [[attribute(3)]];
#endif
#if !defined(HAS_UNIFORM_u_pattern_from)
ushort4 pattern_from [[attribute(3)]];
ushort4 pattern_from [[attribute(4)]];
#endif
#if !defined(HAS_UNIFORM_u_pattern_to)
ushort4 pattern_to [[attribute(4)]];
ushort4 pattern_to [[attribute(5)]];
#endif
};
@@ -398,7 +400,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 {
@@ -441,12 +445,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));
@@ -539,7 +545,7 @@ struct VertexStage {
struct OutlineInstance {
short2 pos;
ushort2 ed_discard;
ushort2 decimals_ed;
};
struct FragmentStage {
@@ -570,7 +576,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),
.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));

View File

@@ -450,12 +450,12 @@ enum {
enum {
idFillExtrusionPosVertexAttribute,
idFillExtrusionDecimalsEdAttribute,
#if MLN_USE_FILL_EXTRUSION_INSTANCING
idFillExtrusionOutlinePosAttribute,
idFillExtrusionEdDiscardAttribute,
#else
idFillExtrusionNormalEdVertexAttribute,
idFillExtrusionNormal2DVertexAttribute,
#endif
// Data driven

View File

@@ -19,7 +19,7 @@ template <>
struct ShaderSource<BuiltIn::FillExtrusionShader, gfx::Backend::Type::Vulkan> {
static constexpr const char* name = "FillExtrusionShader";
static const std::array<AttributeInfo, 4> attributes;
static const std::array<AttributeInfo, 5> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 0> textures;
@@ -27,17 +27,18 @@ struct ShaderSource<BuiltIn::FillExtrusionShader, gfx::Backend::Type::Vulkan> {
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<BuiltIn::FillExtrusionPatternShader, gfx::Backend::Type::Vulkan> {
static constexpr const char* name = "FillExtrusionPatternShader";
static const std::array<AttributeInfo, 5> attributes;
static const std::array<AttributeInfo, 6> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 1> textures;
@@ -344,21 +349,22 @@ struct ShaderSource<BuiltIn::FillExtrusionPatternShader, gfx::Backend::Type::Vul
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_base)
layout(location = 1) in vec2 in_base;
layout(location = 2) in vec2 in_base;
#endif
#if !defined(HAS_UNIFORM_u_height)
layout(location = 2) in vec2 in_height;
layout(location = 3) in vec2 in_height;
#endif
#if !defined(HAS_UNIFORM_u_pattern_from)
layout(location = 3) in uvec4 in_pattern_from;
layout(location = 4) in uvec4 in_pattern_from;
#endif
#if !defined(HAS_UNIFORM_u_pattern_to)
layout(location = 4) in uvec4 in_pattern_to;
layout(location = 5) in uvec4 in_pattern_to;
#endif
layout(push_constant) uniform Constants {
@@ -440,9 +446,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)
@@ -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));

View File

@@ -10,22 +10,23 @@ namespace shaders {
template <>
struct ShaderSource<BuiltIn::FillExtrusionShader, gfx::Backend::Type::WebGPU> {
static constexpr const char* name = "FillExtrusionShader";
static const std::array<AttributeInfo, 5> attributes;
static const std::array<AttributeInfo, 6> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 0> textures;
static constexpr auto vertex = R"(
struct VertexInput {
@location(3) position: vec2<i32>,
@location(4) normal_ed: vec4<i32>,
@location(4) decimals_ed: vec2<u32>,
@location(5) normal2d: vec2<i32>,
#ifndef HAS_UNIFORM_u_color
@location(5) color: vec4<f32>,
@location(6) color: vec4<f32>,
#endif
#ifndef HAS_UNIFORM_u_base
@location(6) base: vec2<f32>,
@location(7) base: vec2<f32>,
#endif
#ifndef HAS_UNIFORM_u_height
@location(7) height: vec2<f32>,
@location(8) height: vec2<f32>,
#endif
};
@@ -88,11 +89,13 @@ fn main(in: VertexInput) -> VertexOutput {
let heightValue = max(props.height, 0.0);
#endif
let normal_i = vec3<f32>(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<f32>(vec2<f32>(in.normal2d) / 16384.0, select(0.0, 1.0, in.normal2d.x == 0 && in.normal2d.y == 0));
out.position = drawable.matrix * vec4<f32>(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<f32>(vec2<f32>(in.position) + decimals, z, 1.0);
#ifdef OVERDRAW_INSPECTOR
out.color = vec4<f32>(1.0, 1.0, 1.0, 1.0);
@@ -107,13 +110,12 @@ fn main(in: VertexInput) -> VertexOutput {
color = color + min(vec4<f32>(0.03, 0.03, 0.03, 1.0), vec4<f32>(1.0));
let luminance = dot(color.rgb, vec3<f32>(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<f32> {
template <>
struct ShaderSource<BuiltIn::FillExtrusionPatternShader, gfx::Backend::Type::WebGPU> {
static constexpr const char* name = "FillExtrusionPatternShader";
static const std::array<AttributeInfo, 6> attributes;
static const std::array<AttributeInfo, 7> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 1> textures;
static constexpr auto vertex = R"(
struct VertexInput {
@location(3) position: vec2<i32>,
@location(4) normal_ed: vec4<i32>,
@location(4) decimals_ed: vec2<u32>,
@location(5) normal2d: vec2<i32>,
#ifndef HAS_UNIFORM_u_base
@location(5) base: vec2<f32>,
@location(6) base: vec2<f32>,
#endif
#ifndef HAS_UNIFORM_u_height
@location(6) height: vec2<f32>,
@location(7) height: vec2<f32>,
#endif
#ifndef HAS_UNIFORM_u_pattern_from
@location(7) pattern_from: vec4<u32>,
@location(8) pattern_from: vec4<u32>,
#endif
#ifndef HAS_UNIFORM_u_pattern_to
@location(8) pattern_to: vec4<u32>,
@location(9) pattern_to: vec4<u32>,
#endif
};
@@ -250,16 +253,18 @@ fn main(in: VertexInput) -> VertexOutput {
let heightValue = max(props.height, 0.0);
#endif
let normal_i = vec3<f32>(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<f32>(vec2<f32>(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>(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<f32>(vec2<f32>(in.position) + decimals, z, 1.0);
var patternPos: vec2<f32>;
if (normal_i.x == 1.0 && normal_i.y == 0.0 && normal_i.z == 16384.0) {
patternPos = vec2<f32>(f32(in.position.x), f32(in.position.y));
if (normal.z == 1.0) {
patternPos = vec2<f32>(in.position);
} else {
patternPos = vec2<f32>(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<f32>(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;

View File

@@ -19,6 +19,12 @@ public:
FillExtrusionLayer(const std::string& layerID, const std::string& sourceID);
~FillExtrusionLayer() override;
// Layout properties
static PropertyValue<float> getDefaultFillExtrusionRoundedCornerDistance();
const PropertyValue<float>& getFillExtrusionRoundedCornerDistance() const;
void setFillExtrusionRoundedCornerDistance(const PropertyValue<float>&);
// Paint properties
static PropertyValue<float> getDefaultFillExtrusionBase();

View File

@@ -39,6 +39,17 @@ FillExtrusionLayer::~FillExtrusionLayer() = default;
// Property getters
jni::Local<jni::Object<>> FillExtrusionLayer::getFillExtrusionRoundedCornerDistance(jni::JNIEnv& env) {
using namespace mbgl::android::conversion;
auto layer = layerPtr.get();
if (!layer) {
return std::move(*convert<jni::Local<jni::Object<>>>(
env, style::FillExtrusionLayer::getDefaultFillExtrusionRoundedCornerDistance()));
}
return std::move(
*convert<jni::Local<jni::Object<>>>(env, toFillExtrusionLayer(*layer).getFillExtrusionRoundedCornerDistance()));
}
jni::Local<jni::Object<>> 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<FillExtrusionLayer, jni::String&, jni::String&>,
"initialize",
"finalize",
METHOD(&FillExtrusionLayer::getFillExtrusionRoundedCornerDistance,
"nativeGetFillExtrusionRoundedCornerDistance"),
METHOD(&FillExtrusionLayer::getFillExtrusionOpacityTransition, "nativeGetFillExtrusionOpacityTransition"),
METHOD(&FillExtrusionLayer::setFillExtrusionOpacityTransition, "nativeSetFillExtrusionOpacityTransition"),
METHOD(&FillExtrusionLayer::getFillExtrusionOpacity, "nativeGetFillExtrusionOpacity"),

View File

@@ -26,6 +26,8 @@ public:
// Properties
jni::Local<jni::Object<jni::ObjectTag>> getFillExtrusionRoundedCornerDistance(jni::JNIEnv&);
jni::Local<jni::Object<jni::ObjectTag>> getFillExtrusionOpacity(jni::JNIEnv&);
void setFillExtrusionOpacityTransition(jni::JNIEnv&, jlong duration, jlong delay);
jni::Local<jni::Object<TransitionOptions>> getFillExtrusionOpacityTransition(jni::JNIEnv&);

View File

@@ -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<Float> getFillExtrusionRoundedCornerDistance() {
checkThread();
return (PropertyValue<Float>) new PropertyValue("fill-extrusion-rounded-corner-distance", nativeGetFillExtrusionRoundedCornerDistance());
}
/**
* Get the FillExtrusionOpacity property
*
@@ -382,6 +394,10 @@ public class FillExtrusionLayer extends Layer {
return (PropertyValue<Boolean>) new PropertyValue("fill-extrusion-vertical-gradient", nativeGetFillExtrusionVerticalGradient());
}
@NonNull
@Keep
private native Object nativeGetFillExtrusionRoundedCornerDistance();
@NonNull
@Keep
private native Object nativeGetFillExtrusionOpacity();

View File

@@ -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<Float> 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<Expression> fillExtrusionRoundedCornerDistance(Expression value) {
return new LayoutPropertyValue<>("fill-extrusion-rounded-corner-distance", value);
}
}

View File

@@ -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() {

View File

@@ -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": {

View File

@@ -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

View File

@@ -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));

View File

@@ -22,7 +22,10 @@ std::unique_ptr<Layout> FillExtrusionLayerFactory::createLayout(
std::unique_ptr<GeometryTileLayer> layer,
const std::vector<Immutable<style::LayerProperties>>& group) {
using namespace style;
using LayoutType = PatternLayout<FillExtrusionBucket, FillExtrusionLayerProperties, FillExtrusionPattern>;
using LayoutType = PatternLayout<FillExtrusionBucket,
FillExtrusionLayerProperties,
FillExtrusionPattern,
FillExtrusionLayoutProperties>;
return std::unique_ptr<Layout>(new (std::nothrow)
LayoutType(parameters.bucketParameters, group, std::move(layer), parameters));
}

View File

@@ -6,6 +6,8 @@
#include <mbgl/util/math.hpp>
#include <mbgl/util/constants.hpp>
#include <variant>
#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<std::string, Immutable<style::LayerProperties>>& 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<GeometryCollection, GeometryCollectionFloat> polyVariant = polygon;
double roundedCornerDistance = layout.get<FillExtrusionRoundedCornerDistance>();
if (roundedCornerDistance > 0) {
polyVariant = roundPolygonCorners(polygon, roundedCornerDistance);
}
std::size_t totalVertices = 0;
for (const auto& ring : polygon) {
std::visit(
[&totalVertices](const auto& poly) {
for (const auto& ring : poly) {
totalVertices += ring.size();
if (totalVertices > std::numeric_limits<uint16_t>::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<uint16_t>::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<double>& p1, const std::optional<Point<double>>& 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<double>(p1);
const auto d2 = convertPoint<double>(p2);
const size_t dist = util::dist<uint16_t>(d1, d2);
if (p2) {
const size_t dist = util::dist<uint16_t>(p1, *p2);
if (edgeDistance + dist > static_cast<size_t>(std::numeric_limits<uint16_t>::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<uint16_t>(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<double> perp = util::unit(util::perp(*p2 - p1));
const auto d1 = convertPoint<double>(p1);
const auto d2 = convertPoint<double>(p2);
const Point<double> perp = util::unit(util::perp(d1 - d2));
const size_t dist = util::dist<int16_t>(d1, d2);
if (edgeDistance + dist > static_cast<size_t>(std::numeric_limits<int16_t>::max())) {
const size_t dist = util::dist<uint16_t>(p1, *p2);
if (edgeDistance + dist > static_cast<size_t>(std::numeric_limits<uint16_t>::max())) {
edgeDistance = 0;
}
vertices.emplace_back(FillExtrusionBucket::layoutVertex(
p1, perp.x, perp.y, 0, 0, static_cast<uint16_t>(edgeDistance)));
vertices.emplace_back(FillExtrusionBucket::layoutVertex(
p1, perp.x, perp.y, 0, 1, static_cast<uint16_t>(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<uint16_t>(edgeDistance)));
vertices.emplace_back(FillExtrusionBucket::layoutVertex(
p2, perp.x, perp.y, 0, 1, static_cast<uint16_t>(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<uint32_t> 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<double>(ring[i]);
std::optional<Point<double>> p2 = std::nullopt;
if (i < nVertices - 1) {
p2 = convertPoint<double>(ring[i + 1]);
}
processRingPoints(p1, p2, edgeDistance);
}
}
std::vector<uint32_t> indices = mapbox::earcut(polygon);
indices = mapbox::earcut(poly);
},
polyVariant);
std::size_t nIndices = indices.size();
assert(nIndices % 3 == 0);

View File

@@ -18,18 +18,18 @@ using FillExtrusionBinders = PaintPropertyBinders<style::FillExtrusionPaintPrope
using FillExtrusionStaticVertex = gfx::Vertex<TypeList<attributes::pos>>;
#if MLN_USE_FILL_EXTRUSION_INSTANCING
using FillExtrusionLayoutVertex = gfx::Vertex<TypeList<attributes::pos, attributes::ed_discard>>;
using FillExtrusionLayoutVertex = gfx::Vertex<TypeList<attributes::pos, attributes::decimals_ed>>;
#else
using FillExtrusionLayoutVertex = gfx::Vertex<TypeList<attributes::pos, attributes::normal_ed>>;
using FillExtrusionLayoutVertex = gfx::Vertex<TypeList<attributes::pos, attributes::decimals_ed, attributes::normal2d>>;
#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<std::string, Immutable<style::LayerProperties>>&,
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<int16_t> 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<uint16_t>(isDiscarded)
static FillExtrusionLayoutVertex layoutVertex(const Point<double>& p, uint16_t edgeDistance, bool isDiscarded) {
auto intPart = Point<double>(std::floor(p.x), std::floor(p.y));
// Multiply factional part by 2^7 to pack them into integers [0..127]
auto fracPart = convertPoint<uint8_t>((p - intPart) * 128.0);
return FillExtrusionLayoutVertex{
{static_cast<int16_t>(intPart.x), static_cast<int16_t>(intPart.y)},
{ // We pack a bool (`isDiscarded`) indicating whether this instance is discarded
static_cast<uint16_t>((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<int16_t> p, double nx, double ny, double nz, unsigned short t, uint16_t e) {
const auto factor = pow(2, 13);
const Point<double>& p, double nx, double ny, unsigned short t, uint16_t edgeDistance) {
const auto factor = pow(2, 14);
auto intPart = Point<double>(std::floor(p.x), std::floor(p.y));
// Multiply factional part by 2^7 to pack them into integers [0..127]
auto fracPart = convertPoint<uint8_t>((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<int16_t>(floor(nx * factor) * 2 + t),
static_cast<int16_t>(ny * factor * 2),
static_cast<int16_t>(nz * factor * 2),
return FillExtrusionLayoutVertex{
{static_cast<int16_t>(intPart.x), static_cast<int16_t>(intPart.y)},
{// We pack a bool (`t`) indicating whether it is an upper or lower vertex
static_cast<uint16_t>((fracPart.x * 256 + fracPart.y) * 2 + t),
// The edgedistance attribute is used for wrapping fill_extrusion patterns
static_cast<int16_t>(e)}}};
edgeDistance},
// Multiply normal vector components in the 2D plane by 2^14 to pack them into integers
{static_cast<int16_t>(nx * factor), static_cast<int16_t>(ny * factor)}};
}
#endif
PossiblyEvaluatedLayoutProperties layout;
static std::array<float, 3> lightColor(const EvaluatedLight&);
static std::array<float, 3> lightPosition(const EvaluatedLight&, const TransformState&);
static float lightIntensity(const EvaluatedLight&);

View File

@@ -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,

View File

@@ -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 <typename T, std::size_t N>
struct data {

View File

@@ -228,7 +228,8 @@ const std::vector<UniformBlockInfo> FillExtrusionShaderInfo::uniformBlocks = {
};
const std::vector<AttributeInfo> 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<UniformBlockInfo> FillExtrusionPatternShaderInfo::uniformBlock
};
const std::vector<AttributeInfo> 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},

View File

@@ -9,13 +9,14 @@ namespace shaders {
using FillExtrusionShaderSource = ShaderSource<BuiltIn::FillExtrusionShader, gfx::Backend::Type::Metal>;
const std::array<AttributeInfo, 4> FillExtrusionShaderSource::attributes = {
const std::array<AttributeInfo, 5> 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<TextureInfo, 0> FillExtrusionShaderSource::textures = {};
@@ -30,7 +31,7 @@ const std::array<AttributeInfo, 1> FillExtrusionInstancedShaderSource::attribute
};
const std::array<AttributeInfo, 5> 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<TextureInfo, 0> FillExtrusionInstancedShaderSource::textures =
using FillExtrusionPatternShaderSource = ShaderSource<BuiltIn::FillExtrusionPatternShader, gfx::Backend::Type::Metal>;
const std::array<AttributeInfo, 5> FillExtrusionPatternShaderSource::attributes = {
const std::array<AttributeInfo, 6> 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<TextureInfo, 1> FillExtrusionPatternShaderSource::textures = {
TextureInfo{0, idFillExtrusionImageTexture},
@@ -70,7 +72,7 @@ const std::array<AttributeInfo, 1> FillExtrusionPatternInstancedShaderSource::at
};
const std::array<AttributeInfo, 6> 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},

View File

@@ -12,11 +12,12 @@ namespace shaders {
using FillExtrusionShaderSource = ShaderSource<BuiltIn::FillExtrusionShader, gfx::Backend::Type::Vulkan>;
const std::array<AttributeInfo, 4> FillExtrusionShaderSource::attributes = {
const std::array<AttributeInfo, 5> 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<TextureInfo, 0> FillExtrusionShaderSource::textures = {};
@@ -31,7 +32,7 @@ const std::array<AttributeInfo, 1> FillExtrusionInstancedShaderSource::attribute
};
const std::array<AttributeInfo, 5> 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<TextureInfo, 0> FillExtrusionInstancedShaderSource::textures =
using FillExtrusionPatternShaderSource = ShaderSource<BuiltIn::FillExtrusionPatternShader, gfx::Backend::Type::Vulkan>;
const std::array<AttributeInfo, 5> FillExtrusionPatternShaderSource::attributes = {
const std::array<AttributeInfo, 6> 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<TextureInfo, 1> FillExtrusionPatternShaderSource::textures = {
TextureInfo{0, idFillExtrusionImageTexture},
@@ -67,7 +69,7 @@ const std::array<AttributeInfo, 1> FillExtrusionPatternInstancedShaderSource::at
};
const std::array<AttributeInfo, 6> 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},

View File

@@ -10,12 +10,13 @@ namespace shaders {
using FillExtrusionShaderSource = ShaderSource<BuiltIn::FillExtrusionShader, gfx::Backend::Type::WebGPU>;
const std::array<AttributeInfo, 5> FillExtrusionShaderSource::attributes = {
const std::array<AttributeInfo, 6> 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<TextureInfo, 0> FillExtrusionShaderSource::textures = {};
@@ -24,13 +25,14 @@ const std::array<TextureInfo, 0> FillExtrusionShaderSource::textures = {};
using FillExtrusionPatternShaderSource = ShaderSource<BuiltIn::FillExtrusionPatternShader, gfx::Backend::Type::WebGPU>;
const std::array<AttributeInfo, 6> FillExtrusionPatternShaderSource::attributes = {
const std::array<AttributeInfo, 7> 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<TextureInfo, 1> FillExtrusionPatternShaderSource::textures = {
TextureInfo{0, idFillExtrusionImageTexture},

View File

@@ -58,11 +58,27 @@ std::unique_ptr<Layer> FillExtrusionLayer::cloneRef(const std::string& id_) cons
return std::make_unique<FillExtrusionLayer>(std::move(impl_));
}
void FillExtrusionLayer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const {
void FillExtrusionLayer::Impl::stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>& writer) const {
layout.stringify(writer);
}
// Layout properties
PropertyValue<float> FillExtrusionLayer::getDefaultFillExtrusionRoundedCornerDistance() {
return FillExtrusionRoundedCornerDistance::defaultValue();
}
const PropertyValue<float>& FillExtrusionLayer::getFillExtrusionRoundedCornerDistance() const {
return impl().layout.get<FillExtrusionRoundedCornerDistance>();
}
void FillExtrusionLayer::setFillExtrusionRoundedCornerDistance(const PropertyValue<float>& value) {
if (value == getFillExtrusionRoundedCornerDistance()) return;
auto impl_ = mutableImpl();
impl_->layout.get<FillExtrusionRoundedCornerDistance>() = 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 <typename T>
@@ -328,7 +345,8 @@ constexpr const auto layerProperties = mapbox::eternal::hash_map<mapbox::eternal
{"fill-extrusion-pattern-transition", toUint8(Property::FillExtrusionPatternTransition)},
{"fill-extrusion-translate-transition", toUint8(Property::FillExtrusionTranslateTransition)},
{"fill-extrusion-translate-anchor-transition", toUint8(Property::FillExtrusionTranslateAnchorTransition)},
{"fill-extrusion-vertical-gradient-transition", toUint8(Property::FillExtrusionVerticalGradientTransition)}});
{"fill-extrusion-vertical-gradient-transition", toUint8(Property::FillExtrusionVerticalGradientTransition)},
{"fill-extrusion-rounded-corner-distance", toUint8(Property::FillExtrusionRoundedCornerDistance)}});
StyleProperty getLayerProperty(const FillExtrusionLayer& layer, Property property) {
switch (property) {
@@ -364,6 +382,8 @@ StyleProperty getLayerProperty(const FillExtrusionLayer& layer, Property propert
return makeStyleProperty(layer.getFillExtrusionTranslateAnchorTransition());
case Property::FillExtrusionVerticalGradientTransition:
return makeStyleProperty(layer.getFillExtrusionVerticalGradientTransition());
case Property::FillExtrusionRoundedCornerDistance:
return makeStyleProperty(layer.getFillExtrusionRoundedCornerDistance());
}
return {};
}
@@ -422,16 +442,23 @@ std::optional<Error> 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<PropertyValue<float>>(value, error, false, false);
if (!typedValue) {
return error;
}
if (property == Property::FillExtrusionOpacity) {
setFillExtrusionOpacity(*typedValue);
return std::nullopt;
}
if (property == Property::FillExtrusionRoundedCornerDistance) {
setFillExtrusionRoundedCornerDistance(*typedValue);
return std::nullopt;
}
}
if (property == Property::FillExtrusionPattern) {
Error error;
const auto& typedValue = convert<PropertyValue<expression::Image>>(value, error, true, false);

View File

@@ -14,7 +14,7 @@ public:
bool hasLayoutDifference(const Layer::Impl&) const override;
void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override;
Properties<>::Unevaluated layout;
FillExtrusionLayoutProperties::Unevaluated layout;
FillExtrusionPaintProperties::Transitionable paint;
DECLARE_LAYER_TYPE_INFO;

View File

@@ -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

View File

@@ -16,6 +16,11 @@
namespace mbgl {
namespace style {
struct FillExtrusionRoundedCornerDistance : LayoutProperty<float> {
static constexpr const char *name() { return "fill-extrusion-rounded-corner-distance"; }
static float defaultValue() { return 0.f; }
};
struct FillExtrusionBase : DataDrivenPaintProperty<float, attributes::base, uniforms::base> {
static float defaultValue() { return 0.f; }
};
@@ -48,6 +53,10 @@ struct FillExtrusionVerticalGradient : PaintProperty<bool> {
static bool defaultValue() { return true; }
};
class FillExtrusionLayoutProperties : public Properties<
FillExtrusionRoundedCornerDistance
> {};
class FillExtrusionPaintProperties : public Properties<
FillExtrusionBase,
FillExtrusionColor,

View File

@@ -1,7 +1,9 @@
#include <mbgl/tile/geometry_tile_data.hpp>
#include <mbgl/tile/tile_id.hpp>
#include <mbgl/math/angles.hpp>
#include <mbgl/math/clamp.hpp>
#include <mbgl/util/instrumentation.hpp>
#include <mbgl/util/math.hpp>
#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<double>(ring[(i - 1 + nVertices) % nVertices]);
auto cornerPoint = convertPoint<double>(ring[i]);
auto nextPoint = convertPoint<double>(ring[(i + 1) % nVertices]);
// Compute the start and end points of the rounded corner
auto edge1Vector = util::normal<double>(prevPoint, cornerPoint);
auto edge2Vector = util::normal<double>(cornerPoint, nextPoint);
auto edge1MaxCornerDistance = util::dist<double>(cornerPoint, prevPoint) * maxEdgeLenPercent;
auto edge2MaxCornerDistance = util::dist<double>(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<float>(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<float>(startPoint));
// Generate points along the arc
auto radius = util::dist<double>(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<double>(centerPoint.x + std::cos(angle) * radius,
centerPoint.y + std::sin(angle) * radius);
roundedCornerRing.emplace_back(convertPoint<float>(arcPoint));
}
// Add the end point of the rounded corner
roundedCornerRing.emplace_back(convertPoint<float>(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

View File

@@ -145,4 +145,10 @@ struct ToGeometryCollection {
}
};
using GeometryCoordinateFloat = Point<float>;
using GeometryCoordinatesFloat = std::vector<GeometryCoordinateFloat>;
using GeometryCollectionFloat = std::vector<GeometryCoordinatesFloat>;
GeometryCollectionFloat roundPolygonCorners(GeometryCollection& polygon, double radius);
} // namespace mbgl

View File

@@ -12,6 +12,16 @@ namespace util {
// TODO: split this file up into individual headers, following mbgl/math/*.hpp.
template <typename T = double, typename S>
T dotProduct(const Point<S>& a, const Point<S>& b) {
return a.x * b.x + a.y * b.y;
}
template <typename T = double, typename S>
T crossProduct(const Point<S>& a, const Point<S>& 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 <typename T = double, typename S>