#include #include #include #include #include #include #include #include #include #include #include #include #include namespace mbgl { using namespace style; RenderHeatmapLayer::RenderHeatmapLayer(Immutable _impl) : RenderLayer(std::move(_impl)), unevaluated(impl().paint.untransitioned()), colorRamp({256, 1}) { } const style::HeatmapLayer::Impl& RenderHeatmapLayer::impl() const { return static_cast(*baseImpl); } void RenderHeatmapLayer::transition(const TransitionParameters& parameters) { unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated)); } void RenderHeatmapLayer::evaluate(const PropertyEvaluationParameters& parameters) { evaluated = unevaluated.evaluate(parameters); passes = (evaluated.get() > 0) ? (RenderPass::Translucent | RenderPass::Pass3D) : RenderPass::None; } bool RenderHeatmapLayer::hasTransition() const { return unevaluated.hasTransition(); } bool RenderHeatmapLayer::hasCrossfade() const { return false; } void RenderHeatmapLayer::render(PaintParameters& parameters, RenderSource*) { if (parameters.pass == RenderPass::Opaque) { return; } if (parameters.pass == RenderPass::Pass3D) { const auto& viewportSize = parameters.staticData.backendSize; const auto size = Size{viewportSize.width / 4, viewportSize.height / 4}; if (!renderTexture || renderTexture->getSize() != size) { if (parameters.context.supportsHalfFloatTextures) { renderTexture = OffscreenTexture(parameters.context, size, gl::TextureType::HalfFloat); try { renderTexture->bind(); } catch (const std::runtime_error& ex) { // can't render to a half-float texture; falling back to unsigned byte one renderTexture = nullopt; parameters.context.supportsHalfFloatTextures = false; } } if (!parameters.context.supportsHalfFloatTextures || !renderTexture) { renderTexture = OffscreenTexture(parameters.context, size, gl::TextureType::UnsignedByte); renderTexture->bind(); } } else { renderTexture->bind(); } if (!colorRampTexture) { colorRampTexture = parameters.context.createTexture(colorRamp, 1, gl::TextureType::UnsignedByte); } parameters.context.clear(Color{ 0.0f, 0.0f, 0.0f, 1.0f }, {}, {}); for (const RenderTile& tile : renderTiles) { auto bucket_ = tile.tile.getBucket(*baseImpl); if (!bucket_) { continue; } HeatmapBucket& bucket = *bucket_; const auto extrudeScale = tile.id.pixelsToTileUnits(1, parameters.state.getZoom()); const auto stencilMode = parameters.mapMode != MapMode::Continuous ? parameters.stencilModeForClipping(tile.clip) : gfx::StencilMode::disabled(); const auto& paintPropertyBinders = bucket.paintPropertyBinders.at(getID()); auto& programInstance = parameters.programs.getHeatmapLayerPrograms().heatmap.get(evaluated); const auto allUniformValues = programInstance.computeAllUniformValues( HeatmapProgram::UniformValues { uniforms::u_intensity::Value( evaluated.get() ), uniforms::u_matrix::Value( tile.matrix ), uniforms::heatmap::u_extrude_scale::Value( extrudeScale ) }, paintPropertyBinders, evaluated, parameters.state.getZoom() ); const auto allAttributeBindings = programInstance.computeAllAttributeBindings( *bucket.vertexBuffer, paintPropertyBinders, evaluated ); checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings)); programInstance.draw( parameters.context, gfx::Triangles(), parameters.depthModeForSublayer(0, gfx::DepthMaskType::ReadOnly), stencilMode, gfx::ColorMode::additive(), gl::CullFaceMode::disabled(), *bucket.indexBuffer, bucket.segments, allUniformValues, allAttributeBindings, getID() ); } } else if (parameters.pass == RenderPass::Translucent) { parameters.context.bindTexture(renderTexture->getTexture(), 0, gl::TextureFilter::Linear); parameters.context.bindTexture(*colorRampTexture, 1, gl::TextureFilter::Linear); const auto& size = parameters.staticData.backendSize; mat4 viewportMat; matrix::ortho(viewportMat, 0, size.width, size.height, 0, 0, 1); const Properties<>::PossiblyEvaluated properties; const HeatmapTextureProgram::Binders paintAttributeData{ properties, 0 }; auto& programInstance = parameters.programs.getHeatmapLayerPrograms().heatmapTexture; const auto allUniformValues = programInstance.computeAllUniformValues( HeatmapTextureProgram::UniformValues{ uniforms::u_matrix::Value( viewportMat ), uniforms::u_world::Value( size ), uniforms::u_image::Value( 0 ), uniforms::u_color_ramp::Value( 1 ), uniforms::u_opacity::Value( evaluated.get() ) }, paintAttributeData, properties, parameters.state.getZoom() ); const auto allAttributeBindings = programInstance.computeAllAttributeBindings( parameters.staticData.extrusionTextureVertexBuffer, paintAttributeData, properties ); checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings)); programInstance.draw( parameters.context, gfx::Triangles(), gfx::DepthMode::disabled(), gfx::StencilMode::disabled(), parameters.colorModeForRenderPass(), gl::CullFaceMode::disabled(), parameters.staticData.quadTriangleIndexBuffer, parameters.staticData.extrusionTextureSegments, allUniformValues, allAttributeBindings, getID() ); } } void RenderHeatmapLayer::update() { updateColorRamp(); } void RenderHeatmapLayer::updateColorRamp() { auto colorValue = unevaluated.get().getValue(); if (colorValue.isUndefined()) { colorValue = HeatmapLayer::getDefaultHeatmapColor(); } const auto length = colorRamp.bytes(); for (uint32_t i = 0; i < length; i += 4) { const auto color = colorValue.evaluate(static_cast(i) / length); colorRamp.data[i + 0] = std::floor(color.r * 255); colorRamp.data[i + 1] = std::floor(color.g * 255); colorRamp.data[i + 2] = std::floor(color.b * 255); colorRamp.data[i + 3] = std::floor(color.a * 255); } if (colorRampTexture) { colorRampTexture = nullopt; } } bool RenderHeatmapLayer::queryIntersectsFeature( const GeometryCoordinates& queryGeometry, const GeometryTileFeature& feature, const float zoom, const TransformState&, const float pixelsToTileUnits, const mat4&) const { (void) queryGeometry; (void) feature; (void) zoom; (void) pixelsToTileUnits; return false; } } // namespace mbgl