Triangulated fill outlines (Metal) (#1880)
This commit is contained in:
@@ -31,6 +31,7 @@
|
||||
#include <mbgl/shaders/fill_layer_ubo.hpp>
|
||||
#include <mbgl/shaders/shader_program_base.hpp>
|
||||
#include <mbgl/util/string_indexer.hpp>
|
||||
#include <mbgl/shaders/line_layer_ubo.hpp>
|
||||
#endif
|
||||
|
||||
namespace mbgl {
|
||||
@@ -42,6 +43,9 @@ namespace {
|
||||
|
||||
#if MLN_DRAWABLE_RENDERER
|
||||
constexpr auto FillShaderName = "FillShader";
|
||||
#if MLN_TRIANGULATE_FILL_OUTLINES
|
||||
constexpr auto FillOutlineTriangulatedShaderName = "LineBasicShader";
|
||||
#endif
|
||||
constexpr auto FillOutlineShaderName = "FillOutlineShader";
|
||||
constexpr auto FillPatternShaderName = "FillPatternShader";
|
||||
constexpr auto FillOutlinePatternShaderName = "FillOutlinePatternShader";
|
||||
@@ -109,6 +113,49 @@ bool RenderFillLayer::hasCrossfade() const {
|
||||
return getCrossfade<FillLayerProperties>(evaluatedProperties).t != 1;
|
||||
}
|
||||
|
||||
#if MLN_TRIANGULATE_FILL_OUTLINES
|
||||
void RenderFillLayer::markLayerRenderable(bool willRender, UniqueChangeRequestVec& changes) {
|
||||
RenderLayer::markLayerRenderable(willRender, changes);
|
||||
if (outlineLayerGroup) {
|
||||
activateLayerGroup(outlineLayerGroup, willRender, changes);
|
||||
}
|
||||
}
|
||||
|
||||
void RenderFillLayer::layerRemoved(UniqueChangeRequestVec& changes) {
|
||||
RenderLayer::layerRemoved(changes);
|
||||
if (outlineLayerGroup) {
|
||||
activateLayerGroup(outlineLayerGroup, false, changes);
|
||||
}
|
||||
}
|
||||
|
||||
void RenderFillLayer::layerIndexChanged(int32_t newLayerIndex, UniqueChangeRequestVec& changes) {
|
||||
RenderLayer::layerIndexChanged(newLayerIndex, changes);
|
||||
|
||||
changeLayerIndex(outlineLayerGroup, newLayerIndex, changes);
|
||||
}
|
||||
|
||||
std::size_t RenderFillLayer::removeAllDrawables() {
|
||||
auto count = RenderLayer::removeAllDrawables();
|
||||
if (outlineLayerGroup) {
|
||||
auto count2 = outlineLayerGroup->clearDrawables();
|
||||
stats.drawablesRemoved += count2;
|
||||
count += count2;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
std::size_t RenderFillLayer::removeTile(RenderPass renderPass, const OverscaledTileID& tileID) {
|
||||
RenderLayer::removeTile(renderPass, tileID);
|
||||
if (const auto tileGroup = static_cast<TileLayerGroup*>(outlineLayerGroup.get())) {
|
||||
const auto n = tileGroup->removeDrawables(renderPass, tileID).size();
|
||||
stats.drawablesRemoved += n;
|
||||
return n;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // MLN_TRIANGULATE_FILL_OUTLINES
|
||||
|
||||
#if MLN_LEGACY_RENDERER
|
||||
void RenderFillLayer::render(PaintParameters& parameters) {
|
||||
assert(renderTiles);
|
||||
@@ -188,7 +235,7 @@ void RenderFillLayer::render(PaintParameters& parameters) {
|
||||
parameters.depthModeForSublayer(unevaluated.get<FillOutlineColor>().isUndefined() ? 2 : 0,
|
||||
gfx::DepthMaskType::ReadOnly),
|
||||
*bucket.lineIndexBuffer,
|
||||
bucket.lineSegments,
|
||||
bucket.basicLineSegments,
|
||||
FillOutlineProgram::TextureBindings{});
|
||||
}
|
||||
}
|
||||
@@ -269,7 +316,7 @@ void RenderFillLayer::render(PaintParameters& parameters) {
|
||||
gfx::Lines{2.0f},
|
||||
parameters.depthModeForSublayer(2, gfx::DepthMaskType::ReadOnly),
|
||||
*bucket.lineIndexBuffer,
|
||||
bucket.lineSegments,
|
||||
bucket.basicLineSegments,
|
||||
FillOutlinePatternProgram::TextureBindings{
|
||||
tile.getIconAtlasTextureBinding(gfx::TextureFilterType::Linear),
|
||||
});
|
||||
@@ -299,6 +346,62 @@ bool RenderFillLayer::queryIntersectsFeature(const GeometryCoordinates& queryGeo
|
||||
|
||||
#if MLN_DRAWABLE_RENDERER
|
||||
|
||||
#if MLN_TRIANGULATE_FILL_OUTLINES
|
||||
class OutlineDrawableTweaker : public gfx::DrawableTweaker {
|
||||
public:
|
||||
OutlineDrawableTweaker(Color color_, float opacity_)
|
||||
: color(color_),
|
||||
opacity(opacity_) {}
|
||||
~OutlineDrawableTweaker() override = default;
|
||||
|
||||
void init(gfx::Drawable&) override{};
|
||||
|
||||
void execute(gfx::Drawable& drawable, const PaintParameters& parameters) override {
|
||||
if (!drawable.getTileID().has_value()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const UnwrappedTileID tileID = drawable.getTileID()->toUnwrapped();
|
||||
const auto zoom = parameters.state.getZoom();
|
||||
auto& uniforms = drawable.mutableUniformBuffers();
|
||||
|
||||
static const StringIdentity idLineUBOName = stringIndexer().get("LineBasicUBO");
|
||||
{
|
||||
const auto matrix = LayerTweaker::getTileMatrix(
|
||||
tileID, parameters, {{0, 0}}, style::TranslateAnchorType::Viewport, false, false, false);
|
||||
|
||||
const shaders::LineBasicUBO lineUBO{
|
||||
/*matrix = */ util::cast<float>(matrix),
|
||||
/*units_to_pixels = */ {1.0f / parameters.pixelsToGLUnits[0], 1.0f / parameters.pixelsToGLUnits[1]},
|
||||
/*ratio = */ 1.0f / tileID.pixelsToTileUnits(1.0f, zoom),
|
||||
/*device_pixel_ratio = */ parameters.pixelRatio};
|
||||
parameters.context.emplaceOrUpdateUniformBuffer(lineUniformBuffer, &lineUBO);
|
||||
}
|
||||
uniforms.addOrReplace(idLineUBOName, lineUniformBuffer);
|
||||
|
||||
static const StringIdentity idLinePropertiesUBOName = stringIndexer().get("LineBasicPropertiesUBO");
|
||||
if (!linePropertiesUniformBuffer) {
|
||||
const shaders::LineBasicPropertiesUBO linePropertiesUBO{/*color =*/color,
|
||||
/*opacity =*/opacity,
|
||||
/*width =*/1.f,
|
||||
0,
|
||||
0};
|
||||
parameters.context.emplaceOrUpdateUniformBuffer(linePropertiesUniformBuffer, &linePropertiesUBO);
|
||||
}
|
||||
if (!uniforms.get(idLinePropertiesUBOName)) {
|
||||
uniforms.addOrReplace(idLinePropertiesUBOName, linePropertiesUniformBuffer);
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
Color color;
|
||||
float opacity;
|
||||
|
||||
gfx::UniformBufferPtr lineUniformBuffer;
|
||||
gfx::UniformBufferPtr linePropertiesUniformBuffer;
|
||||
};
|
||||
#endif // MLN_TRIANGULATE_FILL_OUTLINES
|
||||
|
||||
void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
|
||||
gfx::Context& context,
|
||||
const TransformState& state,
|
||||
@@ -312,7 +415,7 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
|
||||
return;
|
||||
}
|
||||
|
||||
// Set up a layer group
|
||||
// Set up a layer group for fill
|
||||
if (!layerGroup) {
|
||||
if (auto layerGroup_ = context.createTileLayerGroup(layerIndex, /*initialCapacity=*/64, getID())) {
|
||||
setLayerGroup(std::move(layerGroup_), changes);
|
||||
@@ -320,7 +423,19 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
|
||||
return;
|
||||
}
|
||||
}
|
||||
auto* tileLayerGroup = static_cast<TileLayerGroup*>(layerGroup.get());
|
||||
auto* fillTileLayerGroup = static_cast<TileLayerGroup*>(layerGroup.get());
|
||||
|
||||
#if MLN_TRIANGULATE_FILL_OUTLINES
|
||||
// Set up a layer group for outlines
|
||||
if (!outlineLayerGroup) {
|
||||
outlineLayerGroup = context.createTileLayerGroup(layerIndex, /*initialCapacity=*/64, getID());
|
||||
activateLayerGroup(outlineLayerGroup, isRenderable, changes);
|
||||
}
|
||||
auto* outlineTileLayerGroup = static_cast<TileLayerGroup*>(outlineLayerGroup.get());
|
||||
if (!outlineTriangulatedShaderGroup) {
|
||||
outlineTriangulatedShaderGroup = shaders.getShaderGroup(std::string(FillOutlineTriangulatedShaderName));
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!layerTweaker) {
|
||||
layerTweaker = std::make_shared<FillLayerTweaker>(getID(), evaluatedProperties);
|
||||
@@ -358,13 +473,23 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
|
||||
builder.setEnableStencil(true);
|
||||
};
|
||||
|
||||
stats.drawablesRemoved += tileLayerGroup->removeDrawablesIf([&](gfx::Drawable& drawable) {
|
||||
#if MLN_TRIANGULATE_FILL_OUTLINES
|
||||
for (auto layer : {fillTileLayerGroup, outlineTileLayerGroup}) {
|
||||
stats.drawablesRemoved += layer->removeDrawablesIf([&](gfx::Drawable& drawable) {
|
||||
// If the render pass has changed or the tile has dropped out of the cover set, remove it.
|
||||
const auto& tileID = drawable.getTileID();
|
||||
return tileID && !hasRenderTile(*tileID);
|
||||
});
|
||||
}
|
||||
#else
|
||||
stats.drawablesRemoved += fillTileLayerGroup->removeDrawablesIf([&](gfx::Drawable& drawable) {
|
||||
// If the render pass has changed or the tile has dropped out of the cover set, remove it.
|
||||
const auto& tileID = drawable.getTileID();
|
||||
return tileID && !hasRenderTile(*tileID);
|
||||
});
|
||||
#endif
|
||||
|
||||
tileLayerGroup->setStencilTiles(renderTiles);
|
||||
fillTileLayerGroup->setStencilTiles(renderTiles);
|
||||
|
||||
std::unordered_set<StringIdentity> propertiesAsUniforms;
|
||||
for (const RenderTile& tile : *renderTiles) {
|
||||
@@ -554,16 +679,76 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
|
||||
continue;
|
||||
}
|
||||
|
||||
if (unevaluated.get<FillPattern>().isUndefined()) {
|
||||
// Fill will occur in opaque or translucent pass based on `opaquePassCutoff`.
|
||||
// Outline always occurs in translucent pass, defaults to fill color
|
||||
const auto doOutline = evaluated.get<FillAntialias>();
|
||||
// Outline always occurs in translucent pass, defaults to fill color
|
||||
// Outline does not default to fill in the pattern case
|
||||
const auto doOutline = evaluated.get<FillAntialias>() && (unevaluated.get<FillPattern>().isUndefined() ||
|
||||
unevaluated.get<FillOutlineColor>().isUndefined());
|
||||
#if MLN_TRIANGULATE_FILL_OUTLINES
|
||||
const bool dataDrivenOutline = !evaluated.get<FillOutlineColor>().isConstant() ||
|
||||
!evaluated.get<FillOpacity>().isConstant();
|
||||
#endif
|
||||
|
||||
if (unevaluated.get<FillPattern>().isUndefined()) {
|
||||
// Simple fill
|
||||
if (!fillShaderGroup || (doOutline && !outlineShaderGroup)) {
|
||||
continue;
|
||||
}
|
||||
const auto fillShader = std::static_pointer_cast<gfx::ShaderProgramBase>(
|
||||
fillShaderGroup->getOrCreateShader(context, propertiesAsUniforms));
|
||||
|
||||
#if MLN_TRIANGULATE_FILL_OUTLINES
|
||||
const auto outlineTriangulatedShader = doOutline && !dataDrivenOutline ? [&]() -> auto {
|
||||
static const std::unordered_set<StringIdentity> outlinePropertiesAsUniforms{
|
||||
stringIndexer().get("a_color"),
|
||||
stringIndexer().get("a_opacity"),
|
||||
stringIndexer().get("a_width"),
|
||||
};
|
||||
return std::static_pointer_cast<gfx::ShaderProgramBase>(
|
||||
outlineTriangulatedShaderGroup->getOrCreateShader(context, outlinePropertiesAsUniforms));
|
||||
}()
|
||||
: nullptr;
|
||||
|
||||
auto createOutline = [&](auto& builder, Color color, float opacity) {
|
||||
if (doOutline && builder && bucket.sharedLineIndexes->elements()) {
|
||||
static const StringIdentity idVertexAttribName = stringIndexer().get("a_pos_normal");
|
||||
static const StringIdentity idDataAttribName = stringIndexer().get("a_data");
|
||||
builder->setVertexAttrNameId(idVertexAttribName);
|
||||
builder->setShader(outlineTriangulatedShader);
|
||||
builder->setRawVertices({}, bucket.lineVertices.elements(), gfx::AttributeDataType::Short2);
|
||||
gfx::VertexAttributeArray attrs;
|
||||
if (const auto& attr = attrs.add(idVertexAttribName)) {
|
||||
attr->setSharedRawData(bucket.sharedLineVertices,
|
||||
offsetof(LineLayoutVertex, a1),
|
||||
/*vertexOffset=*/0,
|
||||
sizeof(LineLayoutVertex),
|
||||
gfx::AttributeDataType::Short2);
|
||||
}
|
||||
if (const auto& attr = attrs.add(idDataAttribName)) {
|
||||
attr->setSharedRawData(bucket.sharedLineVertices,
|
||||
offsetof(LineLayoutVertex, a2),
|
||||
/*vertexOffset=*/0,
|
||||
sizeof(LineLayoutVertex),
|
||||
gfx::AttributeDataType::UByte4);
|
||||
}
|
||||
builder->setVertexAttributes(std::move(attrs));
|
||||
builder->setSegments(gfx::Triangles(),
|
||||
bucket.sharedLineIndexes,
|
||||
bucket.lineSegments.data(),
|
||||
bucket.lineSegments.size());
|
||||
|
||||
auto tweaker = std::make_shared<OutlineDrawableTweaker>(color, opacity);
|
||||
|
||||
// finish
|
||||
builder->flush();
|
||||
for (auto& drawable : builder->clearDrawables()) {
|
||||
drawable->setTileID(tileID);
|
||||
drawable->addTweaker(tweaker);
|
||||
outlineTileLayerGroup->addDrawable(renderPass, tileID, std::move(drawable));
|
||||
++stats.drawablesAdded;
|
||||
}
|
||||
}
|
||||
};
|
||||
#endif
|
||||
const auto outlineShader = doOutline
|
||||
? std::static_pointer_cast<gfx::ShaderProgramBase>(
|
||||
outlineShaderGroup->getOrCreateShader(context, propertiesAsUniforms))
|
||||
@@ -586,10 +771,10 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
|
||||
if (doOutline && !outlineBuilder && outlineShader) {
|
||||
if (auto builder = context.createDrawableBuilder(layerPrefix + "fill-outline")) {
|
||||
commonInit(*builder);
|
||||
builder->setLineWidth(2.0f);
|
||||
builder->setDepthType(gfx::DepthMaskType::ReadOnly);
|
||||
builder->setColorMode(gfx::ColorMode::alphaBlended());
|
||||
builder->setLineWidth(2.0f);
|
||||
builder->setSubLayerIndex(unevaluated.get<FillOutlineColor>().isUndefined() ? 2 : 0);
|
||||
builder->setColorMode(gfx::ColorMode::alphaBlended());
|
||||
builder->setRenderPass(RenderPass::Translucent);
|
||||
outlineBuilder = std::move(builder);
|
||||
}
|
||||
@@ -599,6 +784,7 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
|
||||
const StringIdentity interpolateUBONameId,
|
||||
const auto& interpolateUBO,
|
||||
FillVariant type) {
|
||||
builder.setVertexAttrNameId(idPosAttribName);
|
||||
builder.flush();
|
||||
|
||||
for (auto& drawable : builder.clearDrawables()) {
|
||||
@@ -608,19 +794,28 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
|
||||
|
||||
auto& uniforms = drawable->mutableUniformBuffers();
|
||||
uniforms.createOrUpdate(interpolateUBONameId, &interpolateUBO, context);
|
||||
tileLayerGroup->addDrawable(renderPass, tileID, std::move(drawable));
|
||||
fillTileLayerGroup->addDrawable(renderPass, tileID, std::move(drawable));
|
||||
++stats.drawablesAdded;
|
||||
}
|
||||
};
|
||||
|
||||
if (fillBuilder && bucket.sharedTriangles->elements()) {
|
||||
fillBuilder->setShader(fillShader);
|
||||
if (outlineBuilder) {
|
||||
#if MLN_TRIANGULATE_FILL_OUTLINES
|
||||
if (doOutline && dataDrivenOutline && outlineBuilder) {
|
||||
fillBuilder->setVertexAttributes(vertexAttrs);
|
||||
outlineBuilder->setVertexAttributes(std::move(vertexAttrs));
|
||||
} else {
|
||||
fillBuilder->setVertexAttributes(std::move(vertexAttrs));
|
||||
}
|
||||
#else
|
||||
if (doOutline && outlineBuilder) {
|
||||
fillBuilder->setVertexAttributes(vertexAttrs);
|
||||
outlineBuilder->setVertexAttributes(std::move(vertexAttrs));
|
||||
} else {
|
||||
fillBuilder->setVertexAttributes(std::move(vertexAttrs));
|
||||
}
|
||||
#endif
|
||||
fillBuilder->setRawVertices({}, vertexCount, gfx::AttributeDataType::Short2);
|
||||
fillBuilder->setSegments(gfx::Triangles(),
|
||||
bucket.sharedTriangles,
|
||||
@@ -631,24 +826,49 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
|
||||
getFillInterpolateUBO(),
|
||||
FillVariant::Fill);
|
||||
}
|
||||
if (outlineBuilder && bucket.sharedLines->elements()) {
|
||||
|
||||
#if MLN_TRIANGULATE_FILL_OUTLINES
|
||||
if (doOutline && outlineBuilder) {
|
||||
if (!dataDrivenOutline) {
|
||||
outlineBuilder->setSubLayerIndex(unevaluated.get<FillOutlineColor>().isUndefined() ? 2 : 0);
|
||||
createOutline(outlineBuilder,
|
||||
evaluated.get<FillOutlineColor>().constantOr(FillOutlineColor::defaultValue()),
|
||||
evaluated.get<FillOpacity>().constantOr(FillOpacity::defaultValue()));
|
||||
} else {
|
||||
if (bucket.sharedBasicLineIndexes->elements()) {
|
||||
outlineBuilder->setShader(outlineShader);
|
||||
outlineBuilder->setRawVertices({}, vertexCount, gfx::AttributeDataType::Short2);
|
||||
outlineBuilder->setSegments(gfx::Lines(2),
|
||||
bucket.sharedBasicLineIndexes,
|
||||
bucket.basicLineSegments.data(),
|
||||
bucket.basicLineSegments.size());
|
||||
finish(*outlineBuilder,
|
||||
FillLayerTweaker::idFillOutlineInterpolateUBOName,
|
||||
getFillOutlineInterpolateUBO(),
|
||||
FillVariant::FillOutline);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (doOutline && outlineBuilder && bucket.sharedBasicLineIndexes->elements()) {
|
||||
outlineBuilder->setShader(outlineShader);
|
||||
outlineBuilder->setRawVertices({}, vertexCount, gfx::AttributeDataType::Short2);
|
||||
outlineBuilder->setSegments(
|
||||
gfx::Lines(2), bucket.sharedLines, bucket.lineSegments.data(), bucket.lineSegments.size());
|
||||
outlineBuilder->setSegments(gfx::Lines(2),
|
||||
bucket.sharedBasicLineIndexes,
|
||||
bucket.basicLineSegments.data(),
|
||||
bucket.basicLineSegments.size());
|
||||
finish(*outlineBuilder,
|
||||
FillLayerTweaker::idFillOutlineInterpolateUBOName,
|
||||
getFillOutlineInterpolateUBO(),
|
||||
FillVariant::FillOutline);
|
||||
}
|
||||
} else { // FillPattern is defined
|
||||
#endif
|
||||
} else {
|
||||
// Fill with pattern
|
||||
if ((renderPass & RenderPass::Translucent) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Outline does not default to fill in the pattern case
|
||||
const auto doOutline = evaluated.get<FillAntialias>() && unevaluated.get<FillOutlineColor>().isUndefined();
|
||||
|
||||
if (!patternShaderGroup || (doOutline && !outlinePatternShaderGroup)) {
|
||||
continue;
|
||||
}
|
||||
@@ -688,7 +908,7 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
|
||||
patternBuilder->clearTweakers();
|
||||
patternBuilder->addTweaker(getAtlasTweaker());
|
||||
}
|
||||
if (outlinePatternBuilder) {
|
||||
if (doOutline && outlinePatternBuilder) {
|
||||
outlinePatternBuilder->clearTweakers();
|
||||
outlinePatternBuilder->addTweaker(getAtlasTweaker());
|
||||
}
|
||||
@@ -709,7 +929,7 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
|
||||
auto& uniforms = drawable->mutableUniformBuffers();
|
||||
uniforms.createOrUpdate(interpolateNameId, &interpolateUBO, context);
|
||||
uniforms.createOrUpdate(tileUBONameId, &tileUBO, context);
|
||||
tileLayerGroup->addDrawable(renderPass, tileID, std::move(drawable));
|
||||
fillTileLayerGroup->addDrawable(renderPass, tileID, std::move(drawable));
|
||||
++stats.drawablesAdded;
|
||||
}
|
||||
};
|
||||
@@ -717,7 +937,7 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
|
||||
if (patternBuilder && bucket.sharedTriangles->elements()) {
|
||||
patternBuilder->setShader(fillShader);
|
||||
patternBuilder->setRenderPass(renderPass);
|
||||
if (outlinePatternBuilder) {
|
||||
if (doOutline && outlinePatternBuilder) {
|
||||
patternBuilder->setVertexAttributes(vertexAttrs);
|
||||
outlinePatternBuilder->setVertexAttributes(std::move(vertexAttrs));
|
||||
} else {
|
||||
@@ -736,12 +956,15 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
|
||||
getFillPatternTilePropsUBO(),
|
||||
FillVariant::FillPattern);
|
||||
}
|
||||
if (outlinePatternBuilder && bucket.sharedLines->elements()) {
|
||||
|
||||
if (doOutline && outlinePatternBuilder && bucket.sharedBasicLineIndexes->elements()) {
|
||||
outlinePatternBuilder->setShader(outlineShader);
|
||||
outlinePatternBuilder->setRenderPass(renderPass);
|
||||
outlinePatternBuilder->setRawVertices({}, vertexCount, gfx::AttributeDataType::Short2);
|
||||
outlinePatternBuilder->setSegments(
|
||||
gfx::Lines(2), bucket.sharedLines, bucket.lineSegments.data(), bucket.lineSegments.size());
|
||||
outlinePatternBuilder->setSegments(gfx::Lines(2),
|
||||
bucket.sharedBasicLineIndexes,
|
||||
bucket.basicLineSegments.data(),
|
||||
bucket.basicLineSegments.size());
|
||||
|
||||
finish(*outlinePatternBuilder,
|
||||
idFillOutlinePatternInterpolateUBOName,
|
||||
|
||||
Reference in New Issue
Block a user