#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace mbgl { using namespace style; namespace { inline const FillExtrusionLayer::Impl& impl_cast(const Immutable& impl) { assert(impl->getTypeInfo() == FillExtrusionLayer::Impl::staticTypeInfo()); return static_cast(*impl); } } // namespace RenderFillExtrusionLayer::RenderFillExtrusionLayer(Immutable _impl) : RenderLayer(makeMutable(std::move(_impl))), unevaluated(impl_cast(baseImpl).paint.untransitioned()) {} RenderFillExtrusionLayer::~RenderFillExtrusionLayer() = default; void RenderFillExtrusionLayer::transition(const TransitionParameters& parameters) { unevaluated = impl_cast(baseImpl).paint.transitioned(parameters, std::move(unevaluated)); } void RenderFillExtrusionLayer::evaluate(const PropertyEvaluationParameters& parameters) { auto properties = makeMutable( staticImmutableCast(baseImpl), parameters.getCrossfadeParameters(), unevaluated.evaluate(parameters)); passes = (properties->evaluated.get() > 0) ? (RenderPass::Translucent | RenderPass::Pass3D) : RenderPass::None; properties->renderPasses = mbgl::underlying_type(passes); evaluatedProperties = std::move(properties); } bool RenderFillExtrusionLayer::hasTransition() const { return unevaluated.hasTransition(); } bool RenderFillExtrusionLayer::hasCrossfade() const { return getCrossfade(evaluatedProperties).t != 1; } bool RenderFillExtrusionLayer::is3D() const { return true; } void RenderFillExtrusionLayer::render(PaintParameters& parameters) { assert(renderTiles); if (parameters.pass != RenderPass::Translucent) { return; } const auto& evaluated = static_cast(*evaluatedProperties).evaluated; const auto& crossfade = static_cast(*evaluatedProperties).crossfade; if (evaluatedProperties->renderPasses == mbgl::underlying_type(RenderPass::None)) { return; } const auto depthMode = parameters.depthModeFor3D(); auto draw = [&](auto& programInstance, const auto& evaluated_, const auto& crossfade_, const gfx::StencilMode& stencilMode, const gfx::ColorMode& colorMode, const auto& tileBucket, const auto& uniformValues, const optional& patternPositionA, const optional& patternPositionB, const auto& textureBindings, const std::string& uniqueName) { const auto& paintPropertyBinders = tileBucket.paintPropertyBinders.at(getID()); paintPropertyBinders.setPatternParameters(patternPositionA, patternPositionB, crossfade_); const auto allUniformValues = programInstance.computeAllUniformValues( uniformValues, paintPropertyBinders, evaluated_, parameters.state.getZoom() ); const auto allAttributeBindings = programInstance.computeAllAttributeBindings( *tileBucket.vertexBuffer, paintPropertyBinders, evaluated_ ); checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings)); programInstance.draw( parameters.context, *parameters.renderPass, gfx::Triangles(), depthMode, stencilMode, colorMode, gfx::CullFaceMode::backCCW(), *tileBucket.indexBuffer, tileBucket.triangleSegments, allUniformValues, allAttributeBindings, textureBindings, getID() + "/" + uniqueName); }; if (unevaluated.get().isUndefined()) { // Draw solid color extrusions auto drawTiles = [&](const gfx::StencilMode& stencilMode_, const gfx::ColorMode& colorMode_, const std::string& name) { for (const RenderTile& tile : *renderTiles) { const LayerRenderData* renderData = getRenderDataForPass(tile, parameters.pass); if (!renderData) { continue; } auto& bucket = static_cast(*renderData->bucket); draw( parameters.programs.getFillExtrusionLayerPrograms().fillExtrusion, evaluated, crossfade, stencilMode_, colorMode_, bucket, FillExtrusionProgram::layoutUniformValues( tile.translatedClipMatrix(evaluated.get(), evaluated.get(), parameters.state), parameters.state, evaluated.get(), parameters.evaluatedLight, evaluated.get() ), {}, {}, FillExtrusionProgram::TextureBindings{}, name ); } }; if (evaluated.get() == 1) { // Draw opaque extrusions drawTiles(gfx::StencilMode::disabled(), parameters.colorModeForRenderPass(), "color"); } else { // Draw transparent buildings in two passes so that only the closest surface is drawn. // First draw all the extrusions into only the depth buffer. No colors are drawn. drawTiles(gfx::StencilMode::disabled(), gfx::ColorMode::disabled(), "depth"); // Then draw all the extrusions a second time, only coloring fragments if they have the // same depth value as the closest fragment in the previous pass. Use the stencil buffer // to prevent the second draw in cases where we have coincident polygons. drawTiles(parameters.stencilModeFor3D(), parameters.colorModeForRenderPass(), "color"); } } else { // Draw textured extrusions const auto fillPatternValue = evaluated.get().constantOr(mbgl::Faded{"", ""}); auto drawTiles = [&](const gfx::StencilMode& stencilMode_, const gfx::ColorMode& colorMode_, const std::string& name) { for (const RenderTile& tile : *renderTiles) { const LayerRenderData* renderData = getRenderDataForPass(tile, parameters.pass); if (!renderData) { continue; } auto& bucket = static_cast(*renderData->bucket); optional patternPosA = tile.getPattern(fillPatternValue.from.id()); optional patternPosB = tile.getPattern(fillPatternValue.to.id()); draw( parameters.programs.getFillExtrusionLayerPrograms().fillExtrusionPattern, evaluated, crossfade, stencilMode_, colorMode_, bucket, FillExtrusionPatternProgram::layoutUniformValues( tile.translatedClipMatrix(evaluated.get(), evaluated.get(), parameters.state), tile.getIconAtlasTexture().size, crossfade, tile.id, parameters.state, evaluated.get(), -std::pow(2, tile.id.canonical.z) / util::tileSize / 8.0f, parameters.pixelRatio, parameters.evaluatedLight, evaluated.get() ), patternPosA, patternPosB, FillExtrusionPatternProgram::TextureBindings{ textures::image::Value{ tile.getIconAtlasTexture().getResource(), gfx::TextureFilterType::Linear }, }, name ); } }; // Draw transparent buildings in two passes so that only the closest surface is drawn. // First draw all the extrusions into only the depth buffer. No colors are drawn. drawTiles(gfx::StencilMode::disabled(), gfx::ColorMode::disabled(), "depth"); // Then draw all the extrusions a second time, only coloring fragments if they have the // same depth value as the closest fragment in the previous pass. Use the stencil buffer // to prevent the second draw in cases where we have coincident polygons. drawTiles(parameters.stencilModeFor3D(), parameters.colorModeForRenderPass(), "color"); } } bool RenderFillExtrusionLayer::queryIntersectsFeature(const GeometryCoordinates& queryGeometry, const GeometryTileFeature& feature, const float, const TransformState& transformState, const float pixelsToTileUnits, const mat4&, const FeatureState&) const { const auto& evaluated = static_cast(*evaluatedProperties).evaluated; auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry( queryGeometry, evaluated.get(), evaluated.get(), transformState.getBearing(), pixelsToTileUnits); return util::polygonIntersectsMultiPolygon(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries()); } } // namespace mbgl