1 #include <mbgl/renderer/layers/render_hillshade_layer.hpp>
2 #include <mbgl/renderer/buckets/hillshade_bucket.hpp>
3 #include <mbgl/renderer/render_tile.hpp>
4 #include <mbgl/renderer/sources/render_raster_dem_source.hpp>
5 #include <mbgl/renderer/paint_parameters.hpp>
6 #include <mbgl/renderer/render_static_data.hpp>
7 #include <mbgl/programs/programs.hpp>
8 #include <mbgl/programs/hillshade_program.hpp>
9 #include <mbgl/programs/hillshade_prepare_program.hpp>
10 #include <mbgl/tile/tile.hpp>
11 #include <mbgl/style/layers/hillshade_layer_impl.hpp>
12 #include <mbgl/util/geo.hpp>
13 #include <mbgl/util/offscreen_texture.hpp>
14 
15 namespace mbgl {
16 
17 using namespace style;
RenderHillshadeLayer(Immutable<style::HillshadeLayer::Impl> _impl)18 RenderHillshadeLayer::RenderHillshadeLayer(Immutable<style::HillshadeLayer::Impl> _impl)
19     : RenderLayer(style::LayerType::Hillshade, _impl),
20       unevaluated(impl().paint.untransitioned()) {
21 }
22 
impl() const23 const style::HillshadeLayer::Impl& RenderHillshadeLayer::impl() const {
24     return static_cast<const style::HillshadeLayer::Impl&>(*baseImpl);
25 }
26 
createBucket(const BucketParameters &,const std::vector<const RenderLayer * > &) const27 std::unique_ptr<Bucket> RenderHillshadeLayer::createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const {
28     assert(false);
29     return nullptr;
30 }
31 
getLatRange(const UnwrappedTileID & id)32 const std::array<float, 2> RenderHillshadeLayer::getLatRange(const UnwrappedTileID& id) {
33    const LatLng latlng0 = LatLng(id);
34    const LatLng latlng1 = LatLng(UnwrappedTileID(id.canonical.z, id.canonical.x, id.canonical.y + 1));
35    return {{ (float)latlng0.latitude(), (float)latlng1.latitude() }};
36 }
37 
getLight(const PaintParameters & parameters)38 const std::array<float, 2> RenderHillshadeLayer::getLight(const PaintParameters& parameters){
39     float azimuthal = evaluated.get<HillshadeIlluminationDirection>() * util::DEG2RAD;
40     if (evaluated.get<HillshadeIlluminationAnchor>() == HillshadeIlluminationAnchorType::Viewport) azimuthal = azimuthal - parameters.state.getAngle();
41     return {{evaluated.get<HillshadeExaggeration>(), azimuthal}};
42 }
43 
transition(const TransitionParameters & parameters)44 void RenderHillshadeLayer::transition(const TransitionParameters& parameters) {
45     unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated));
46 }
47 
evaluate(const PropertyEvaluationParameters & parameters)48 void RenderHillshadeLayer::evaluate(const PropertyEvaluationParameters& parameters) {
49     evaluated = unevaluated.evaluate(parameters);
50     passes = (evaluated.get<style::HillshadeExaggeration >() > 0)
51                  ? (RenderPass::Translucent | RenderPass::Pass3D)
52                  : RenderPass::None;
53 }
54 
hasTransition() const55 bool RenderHillshadeLayer::hasTransition() const {
56     return unevaluated.hasTransition();
57 }
58 
render(PaintParameters & parameters,RenderSource * src)59 void RenderHillshadeLayer::render(PaintParameters& parameters, RenderSource* src) {
60     if (parameters.pass != RenderPass::Translucent && parameters.pass != RenderPass::Pass3D)
61         return;
62 
63     RenderRasterDEMSource* demsrc = dynamic_cast<RenderRasterDEMSource*>(src);
64     const uint8_t TERRAIN_RGB_MAXZOOM = 15;
65     const uint8_t maxzoom = demsrc != nullptr ? demsrc->getMaxZoom() : TERRAIN_RGB_MAXZOOM;
66 
67     auto draw = [&] (const mat4& matrix,
68                      const auto& vertexBuffer,
69                      const auto& indexBuffer,
70                      const auto& segments,
71                      const UnwrappedTileID& id) {
72         auto& programInstance = parameters.programs.hillshade;
73 
74         const HillshadeProgram::PaintPropertyBinders paintAttributeData{ evaluated, 0 };
75 
76         const auto allUniformValues = programInstance.computeAllUniformValues(
77             HillshadeProgram::UniformValues {
78                 uniforms::u_matrix::Value{ matrix },
79                 uniforms::u_image::Value{ 0 },
80                 uniforms::u_highlight::Value{ evaluated.get<HillshadeHighlightColor>() },
81                 uniforms::u_shadow::Value{ evaluated.get<HillshadeShadowColor>() },
82                 uniforms::u_accent::Value{ evaluated.get<HillshadeAccentColor>() },
83                 uniforms::u_light::Value{ getLight(parameters) },
84                 uniforms::u_latrange::Value{ getLatRange(id) },
85             },
86             paintAttributeData,
87             evaluated,
88             parameters.state.getZoom()
89         );
90         const auto allAttributeBindings = programInstance.computeAllAttributeBindings(
91             vertexBuffer,
92             paintAttributeData,
93             evaluated
94         );
95 
96         checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings));
97 
98         programInstance.draw(
99             parameters.context,
100             gl::Triangles(),
101             parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly),
102             gl::StencilMode::disabled(),
103             parameters.colorModeForRenderPass(),
104             indexBuffer,
105             segments,
106             allUniformValues,
107             allAttributeBindings,
108             getID()
109         );
110     };
111 
112     mat4 mat;
113     matrix::ortho(mat, 0, util::EXTENT, -util::EXTENT, 0, 0, 1);
114     matrix::translate(mat, mat, 0, -util::EXTENT, 0);
115 
116     for (const RenderTile& tile : renderTiles) {
117         auto bucket_ = tile.tile.getBucket<HillshadeBucket>(*baseImpl);
118         if (!bucket_) {
119             continue;
120         }
121         HillshadeBucket& bucket = *bucket_;
122 
123         if (!bucket.hasData()){
124             continue;
125         }
126 
127         if (!bucket.isPrepared() && parameters.pass == RenderPass::Pass3D) {
128             const uint16_t tilesize = bucket.getDEMData().dim;
129             OffscreenTexture view(parameters.context, { tilesize, tilesize });
130             view.bind();
131 
132             parameters.context.bindTexture(*bucket.dem, 0, gl::TextureFilter::Nearest, gl::TextureMipMap::No, gl::TextureWrap::Clamp, gl::TextureWrap::Clamp);
133             const Properties<>::PossiblyEvaluated properties;
134             const HillshadePrepareProgram::PaintPropertyBinders paintAttributeData{ properties, 0 };
135 
136             auto& programInstance = parameters.programs.hillshadePrepare;
137 
138             const auto allUniformValues = programInstance.computeAllUniformValues(
139                 HillshadePrepareProgram::UniformValues {
140                     uniforms::u_matrix::Value { mat },
141                     uniforms::u_dimension::Value { {{uint16_t(tilesize * 2), uint16_t(tilesize * 2) }} },
142                     uniforms::u_zoom::Value{ float(tile.id.canonical.z) },
143                     uniforms::u_maxzoom::Value{ float(maxzoom) },
144                     uniforms::u_image::Value{ 0 }
145                 },
146                 paintAttributeData,
147                 properties,
148                 parameters.state.getZoom()
149             );
150             const auto allAttributeBindings = programInstance.computeAllAttributeBindings(
151                 parameters.staticData.rasterVertexBuffer,
152                 paintAttributeData,
153                 properties
154             );
155 
156             checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings));
157 
158             programInstance.draw(
159                 parameters.context,
160                 gl::Triangles(),
161                 parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly),
162                 gl::StencilMode::disabled(),
163                 parameters.colorModeForRenderPass(),
164                 parameters.staticData.quadTriangleIndexBuffer,
165                 parameters.staticData.rasterSegments,
166                 allUniformValues,
167                 allAttributeBindings,
168                 getID()
169             );
170             bucket.texture = std::move(view.getTexture());
171             bucket.setPrepared(true);
172         } else if (parameters.pass == RenderPass::Translucent) {
173             assert(bucket.texture);
174             parameters.context.bindTexture(*bucket.texture, 0, gl::TextureFilter::Linear, gl::TextureMipMap::No, gl::TextureWrap::Clamp, gl::TextureWrap::Clamp);
175 
176             if (bucket.vertexBuffer && bucket.indexBuffer && !bucket.segments.empty()) {
177                 // Draw only the parts of the tile that aren't drawn by another tile in the layer.
178                 draw(parameters.matrixForTile(tile.id, true),
179                      *bucket.vertexBuffer,
180                      *bucket.indexBuffer,
181                      bucket.segments,
182                      tile.id);
183             } else {
184                 // Draw the full tile.
185                 draw(parameters.matrixForTile(tile.id, true),
186                      parameters.staticData.rasterVertexBuffer,
187                      parameters.staticData.quadTriangleIndexBuffer,
188                      parameters.staticData.rasterSegments,
189                      tile.id);
190             }
191         }
192 
193 
194     }
195 }
196 
197 } // namespace mbgl
198