Преглед на файлове

Add globe view mode, partially solve tile visibility

* Toggle between ViewMode::Flat and ViewMode::Globe with Ctrl+G
* Approach tile visibility problem at the sphere
Johannes Hofmann преди 7 години
родител
ревизия
696d382fc8
променени са 4 файла, в които са добавени 208 реда и са изтрити 113 реда
  1. 61
    99
      src/globe_tile_layer.rs
  2. 9
    1
      src/main.rs
  3. 103
    1
      src/map_view.rs
  4. 35
    12
      src/map_view_gl.rs

+ 61
- 99
src/globe_tile_layer.rs Целия файл

@@ -1,7 +1,6 @@
1
-use std::f32::consts::{PI, FRAC_1_PI};
2 1
 use std::ffi::CStr;
3 2
 use buffer::{Buffer, DrawMode};
4
-use cgmath::{Matrix3, Point3, Transform, vec3};
3
+use cgmath::Transform;
5 4
 use context::Context;
6 5
 use coord::{LatLonRad, TileCoord, View};
7 6
 use map_view::MapView;
@@ -72,8 +71,8 @@ impl GlobeTileLayer {
72 71
         source: &TileSource,
73 72
         cache: &mut TileCache,
74 73
         tile_atlas: &mut TileAtlas,
75
-        viewport_size: (u32, u32),
76 74
     ) {
75
+        //TODO Add distance function to TileCache that takes topology of the sphere into account.
77 76
         cache.set_view_location(View {
78 77
             source_id: source.id(),
79 78
             zoom: map_view.tile_zoom(),
@@ -82,106 +81,69 @@ impl GlobeTileLayer {
82 81
 
83 82
         let mut vertex_data = vec![];
84 83
 
85
-        let (scale_x, scale_y) = {
86
-            let factor = 2.0f32.powf(map_view.zoom as f32) *
87
-                (FRAC_1_PI * map_view.tile_size as f32);
88
-            (factor / viewport_size.0 as f32, factor / viewport_size.1 as f32)
89
-        };
90
-
91
-        let scale_mat: Matrix3<f32> = Matrix3::from_cols(
92
-            vec3(scale_x, 0.0, 0.0),
93
-            vec3(0.0, scale_y, 0.0),
94
-            vec3(0.0, 0.0, 1.0),
95
-        );
96
-
97
-        let rot_mat_x: Matrix3<f32> = {
98
-            let center_latlon = map_view.center.to_latlon_rad();
99
-            let alpha = center_latlon.lon as f32 + (PI * 0.5);
100
-            let cosa = alpha.cos();
101
-            let sina = alpha.sin();
102
-                Matrix3::from_cols(
103
-                vec3(cosa, 0.0, -sina),
104
-                vec3(0.0, 1.0, 0.0),
105
-                vec3(sina, 0.0, cosa),
106
-            )
107
-        };
108
-
109
-        let rot_mat_y: Matrix3<f32> = {
110
-            let center_latlon = map_view.center.to_latlon_rad();
111
-            let alpha = (-center_latlon.lat) as f32;
112
-            let cosa = alpha.cos();
113
-            let sina = alpha.sin();
114
-                Matrix3::from_cols(
115
-                vec3(1.0, 0.0, 0.0),
116
-                vec3(0.0, cosa, sina),
117
-                vec3(0.0, -sina, cosa),
118
-            )
119
-        };
120
-
121
-        let transform = Transform::<Point3<f32>>::concat(&rot_mat_y, &rot_mat_x);
122
-        let transform = Transform::<Point3<f32>>::concat(&scale_mat, &transform);
84
+        let transform = map_view.globe_transformation_matrix();
123 85
 
124 86
         let (inset_x, inset_y) = tile_atlas.texture_margins();
125 87
 
126
-        for tile_y in 0..8 {
127
-            for tile_x in 0..8 {
128
-                let tc = TileCoord::new(3, tile_x, tile_y);
129
-                let slot = tile_atlas.store(cx, tc, source, cache, true)
130
-                    .unwrap_or_else(TileAtlas::default_slot);
131
-                let texrect = tile_atlas.slot_to_texture_rect(slot);
132
-                let tex_minmax = texrect.inset(inset_x, inset_y);
133
-
134
-                let minmax = [
135
-                    tex_minmax.x1 as f32,
136
-                    tex_minmax.y1 as f32,
137
-                    tex_minmax.x2 as f32,
138
-                    tex_minmax.y2 as f32,
139
-                ];
140
-
141
-                for (tc, sub_tile) in tc.children_iter(3) {
142
-                    let ll_nw = tc.latlon_rad_north_west();
143
-                    let ll_se = {
144
-                        let tc = TileCoord::new(tc.zoom, tc.x + 1, tc.y + 1);
145
-                        tc.latlon_rad_north_west()
146
-                    };
147
-
148
-                    let ll_ne = LatLonRad::new(ll_nw.lat, ll_se.lon);
149
-                    let ll_sw = LatLonRad::new(ll_se.lat, ll_nw.lon);
150
-
151
-                    let p1 = ll_nw.to_sphere_point3(1.0);
152
-                    let p2 = ll_ne.to_sphere_point3(1.0);
153
-                    let p3 = ll_se.to_sphere_point3(1.0);
154
-                    let p4 = ll_sw.to_sphere_point3(1.0);
155
-
156
-                    let p1 = transform.transform_point(p1);
157
-                    let p2 = transform.transform_point(p2);
158
-                    let p3 = transform.transform_point(p3);
159
-                    let p4 = transform.transform_point(p4);
160
-
161
-                    if p1.z > 0.0 && p2.z > 0.0 && p3.z > 0.0 && p4.z > 0.0 {
162
-                        continue;
163
-                    }
164
-
165
-                    let texrect = texrect.subdivide(&sub_tile);
166
-
167
-                    let p1 = [p1.x as f32, p1.y as f32, p1.z as f32, texrect.x1 as f32, texrect.y1 as f32];
168
-                    let p2 = [p2.x as f32, p2.y as f32, p2.z as f32, texrect.x2 as f32, texrect.y1 as f32];
169
-                    let p3 = [p3.x as f32, p3.y as f32, p3.z as f32, texrect.x2 as f32, texrect.y2 as f32];
170
-                    let p4 = [p4.x as f32, p4.y as f32, p4.z as f32, texrect.x1 as f32, texrect.y2 as f32];
171
-
172
-                    vertex_data.extend(&p1);
173
-                    vertex_data.extend(&minmax);
174
-                    vertex_data.extend(&p2);
175
-                    vertex_data.extend(&minmax);
176
-                    vertex_data.extend(&p3);
177
-                    vertex_data.extend(&minmax);
178
-                    vertex_data.extend(&p1);
179
-                    vertex_data.extend(&minmax);
180
-                    vertex_data.extend(&p3);
181
-                    vertex_data.extend(&minmax);
182
-                    vertex_data.extend(&p4);
183
-                    vertex_data.extend(&minmax);
88
+        for tile_coord in map_view.visible_globe_tiles().into_iter() {
89
+            let slot = tile_atlas.store(cx, tile_coord, source, cache, true)
90
+                .unwrap_or_else(TileAtlas::default_slot);
91
+            let texrect = tile_atlas.slot_to_texture_rect(slot);
92
+            let tex_minmax = texrect.inset(inset_x, inset_y);
93
+
94
+            let minmax = [
95
+                tex_minmax.x1 as f32,
96
+                tex_minmax.y1 as f32,
97
+                tex_minmax.x2 as f32,
98
+                tex_minmax.y2 as f32,
99
+            ];
100
+
101
+            let subdivision = 6u32.saturating_sub(tile_coord.zoom).max(2);
102
+
103
+            for (tc, sub_tile) in tile_coord.children_iter(subdivision) {
104
+                let ll_nw = tc.latlon_rad_north_west();
105
+                let ll_se = {
106
+                    let tc = TileCoord::new(tc.zoom, tc.x + 1, tc.y + 1);
107
+                    tc.latlon_rad_north_west()
108
+                };
109
+
110
+                let ll_ne = LatLonRad::new(ll_nw.lat, ll_se.lon);
111
+                let ll_sw = LatLonRad::new(ll_se.lat, ll_nw.lon);
112
+
113
+                let p1 = ll_nw.to_sphere_point3(1.0);
114
+                let p2 = ll_ne.to_sphere_point3(1.0);
115
+                let p3 = ll_se.to_sphere_point3(1.0);
116
+                let p4 = ll_sw.to_sphere_point3(1.0);
117
+
118
+                let p1 = transform.transform_point(p1);
119
+                let p2 = transform.transform_point(p2);
120
+                let p3 = transform.transform_point(p3);
121
+                let p4 = transform.transform_point(p4);
122
+
123
+                // Discard tiles that are facing backwards
124
+                if (p1.z + p3.z) * 0.5 > 0.0 {
125
+                    continue;
184 126
                 }
127
+
128
+                let texrect = texrect.subdivide(&sub_tile);
129
+
130
+                let p1 = [p1.x as f32, p1.y as f32, p1.z as f32, texrect.x1 as f32, texrect.y1 as f32];
131
+                let p2 = [p2.x as f32, p2.y as f32, p2.z as f32, texrect.x2 as f32, texrect.y1 as f32];
132
+                let p3 = [p3.x as f32, p3.y as f32, p3.z as f32, texrect.x2 as f32, texrect.y2 as f32];
133
+                let p4 = [p4.x as f32, p4.y as f32, p4.z as f32, texrect.x1 as f32, texrect.y2 as f32];
134
+
135
+                vertex_data.extend(&p1);
136
+                vertex_data.extend(&minmax);
137
+                vertex_data.extend(&p2);
138
+                vertex_data.extend(&minmax);
139
+                vertex_data.extend(&p3);
140
+                vertex_data.extend(&minmax);
141
+                vertex_data.extend(&p1);
142
+                vertex_data.extend(&minmax);
143
+                vertex_data.extend(&p3);
144
+                vertex_data.extend(&minmax);
145
+                vertex_data.extend(&p4);
146
+                vertex_data.extend(&minmax);
185 147
             }
186 148
         }
187 149
 

+ 9
- 1
src/main.rs Целия файл

@@ -194,6 +194,14 @@ fn handle_event(
194 194
                         }
195 195
                         Action::Redraw
196 196
                     },
197
+                    VirtualKeyCode::G => {
198
+                        if modifiers.ctrl {
199
+                            map.toggle_view_mode();
200
+                            Action::Redraw
201
+                        } else {
202
+                            Action::Nothing
203
+                        }
204
+                    },
197 205
                     _ => Action::Nothing,
198 206
                 }
199 207
             },
@@ -365,7 +373,7 @@ fn run() -> Result<(), Box<Error>> {
365 373
         if redraw {
366 374
             let draw_start = Instant::now();
367 375
 
368
-            if !map.viewport_in_map() {
376
+            if !map.map_covers_viewport() {
369 377
                 cx.clear_color((0.2, 0.2, 0.2, 1.0));
370 378
             }
371 379
             let draw_result = map.draw(&mut cx, sources.current());

+ 103
- 1
src/map_view.rs Целия файл

@@ -1,4 +1,7 @@
1
+use cgmath::{Matrix3, Point3, Transform, vec3};
1 2
 use coord::{MapCoord, ScreenCoord, ScreenRect, TileCoord};
3
+use std::f32::consts::{PI, FRAC_1_PI};
4
+use std::f64;
2 5
 
3 6
 
4 7
 /// A view of a tiled map with a rectangular viewport and a zoom.
@@ -79,7 +82,7 @@ impl MapView {
79 82
     }
80 83
 
81 84
     /// Returns true if the viewport rectangle is fully inside the map.
82
-    pub fn viewport_in_map(&self) -> bool {
85
+    pub fn map_covers_viewport(&self) -> bool {
83 86
         let scale = f64::powf(2.0, -self.zoom) / f64::from(self.tile_size);
84 87
 
85 88
         let y_top = self.center.y + -0.5 * self.height * scale;
@@ -88,6 +91,16 @@ impl MapView {
88 91
         y_top >= 0.0 && y_bottom <= 1.0
89 92
     }
90 93
 
94
+    /// Returns true if the globe rendering covers the whole viewport.
95
+    pub fn globe_covers_viewport(&self) -> bool {
96
+        //TODO Add a little safety margin since the rendered globe is not a perfect sphere and its
97
+        // screen area is underestimated by the tesselation.
98
+        let globe_diameter = 2.0f64.powf(self.zoom) *
99
+            (f64::consts::FRAC_1_PI * self.tile_size as f64);
100
+
101
+        return (self.width * self.width) + (self.height * self.height) < globe_diameter * globe_diameter;
102
+    }
103
+
91 104
     /// Returns the screen coordinate of the top-left corner of a tile.
92 105
     pub fn tile_screen_position(&self, tile: &TileCoord) -> ScreenCoord {
93 106
         self.map_to_screen_coord(tile.map_coord_north_west())
@@ -133,6 +146,95 @@ impl MapView {
133 146
         visible_tiles
134 147
     }
135 148
 
149
+    //TODO Put this in a new module with other "sphere things"
150
+    /// Returns a `Vec` of all tiles that are visible in the current viewport.
151
+    pub fn visible_globe_tiles(&self) -> Vec<TileCoord> {
152
+        let uzoom = self.tile_zoom();
153
+
154
+        match uzoom {
155
+            0 => return vec![TileCoord::new(0, 0, 0)],
156
+            1 => {
157
+                // return every tile
158
+                return vec![
159
+                    TileCoord::new(1, 0, 0),
160
+                    TileCoord::new(1, 0, 1),
161
+                    TileCoord::new(1, 1, 0),
162
+                    TileCoord::new(1, 1, 1),
163
+                ]},
164
+            _ => {},
165
+        }
166
+
167
+        let center_tile = self.center.on_tile_at_zoom(uzoom).globe_norm();
168
+
169
+
170
+        let mut tiles = vec![];
171
+        tiles.push(center_tile);
172
+
173
+        // Check rings of tiles with the same Chebyshev distance to the center_tile
174
+        {
175
+            let zoom_level_tiles = TileCoord::get_zoom_level_tiles(uzoom);
176
+            let max_full_rings = (zoom_level_tiles - 1) / 2;
177
+
178
+            for radius in 1..3.min(max_full_rings + 1) {
179
+                let (rx1, rx2) = (center_tile.x - radius, center_tile.x + radius);
180
+                let (ry1, ry2) = (center_tile.y - radius, center_tile.y + radius);
181
+
182
+                for x in rx1..rx2+1 {
183
+                    tiles.push(TileCoord::new(uzoom, x, ry1).globe_norm());
184
+                    tiles.push(TileCoord::new(uzoom, x, ry2).globe_norm());
185
+                }
186
+                for y in ry1+1..ry2 {
187
+                    tiles.push(TileCoord::new(uzoom, rx1, y).globe_norm());
188
+                    tiles.push(TileCoord::new(uzoom, rx2, y).globe_norm());
189
+                }
190
+            }
191
+        }
192
+
193
+        tiles
194
+    }
195
+
196
+    pub fn globe_transformation_matrix(&self) -> Matrix3<f32> {
197
+        let (scale_x, scale_y) = {
198
+            let factor = 2.0f32.powf(self.zoom as f32) *
199
+                (FRAC_1_PI * self.tile_size as f32);
200
+            (factor / self.width as f32, factor / self.height as f32)
201
+        };
202
+
203
+        let scale_mat: Matrix3<f32> = Matrix3::from_cols(
204
+            vec3(scale_x, 0.0, 0.0),
205
+            vec3(0.0, scale_y, 0.0),
206
+            vec3(0.0, 0.0, 1.0),
207
+        );
208
+
209
+        let rot_mat_x: Matrix3<f32> = {
210
+            let center_latlon = self.center.to_latlon_rad();
211
+            let alpha = center_latlon.lon as f32 + (PI * 0.5);
212
+            let cosa = alpha.cos();
213
+            let sina = alpha.sin();
214
+                Matrix3::from_cols(
215
+                vec3(cosa, 0.0, -sina),
216
+                vec3(0.0, 1.0, 0.0),
217
+                vec3(sina, 0.0, cosa),
218
+            )
219
+        };
220
+
221
+        let rot_mat_y: Matrix3<f32> = {
222
+            let center_latlon = self.center.to_latlon_rad();
223
+            let alpha = (-center_latlon.lat) as f32;
224
+            let cosa = alpha.cos();
225
+            let sina = alpha.sin();
226
+                Matrix3::from_cols(
227
+                vec3(1.0, 0.0, 0.0),
228
+                vec3(0.0, cosa, sina),
229
+                vec3(0.0, -sina, cosa),
230
+            )
231
+        };
232
+
233
+        let transform = Transform::<Point3<f32>>::concat(&rot_mat_y, &rot_mat_x);
234
+        let transform = Transform::<Point3<f32>>::concat(&scale_mat, &transform);
235
+        transform
236
+    }
237
+
136 238
     /// Returns the tile zoom value that is used for rendering with the current zoom.
137 239
     pub fn tile_zoom(&self) -> u32 {
138 240
         (self.zoom + self.tile_zoom_offset).floor().max(0.0) as u32

+ 35
- 12
src/map_view_gl.rs Целия файл

@@ -23,9 +23,16 @@ pub struct MapViewGl {
23 23
     tile_layer: TileLayer,
24 24
     marker_layer: MarkerLayer,
25 25
     globe_tile_layer: GlobeTileLayer,
26
+    view_mode: ViewMode,
26 27
     last_draw_type: DrawType,
27 28
 }
28 29
 
30
+#[derive(Debug, Eq, PartialEq)]
31
+enum ViewMode {
32
+    Flat,
33
+    Globe,
34
+}
35
+
29 36
 #[derive(Debug, Eq, PartialEq)]
30 37
 enum DrawType {
31 38
     Null,
@@ -85,6 +92,7 @@ impl MapViewGl {
85 92
             tile_layer,
86 93
             marker_layer: MarkerLayer::new(cx),
87 94
             globe_tile_layer: GlobeTileLayer::new(cx),
95
+            view_mode: ViewMode::Flat,
88 96
             last_draw_type: DrawType::Null,
89 97
         }
90 98
     }
@@ -99,15 +107,25 @@ impl MapViewGl {
99 107
         self.marker_layer.add_marker(map_coord);
100 108
     }
101 109
 
102
-    pub fn viewport_in_map(&self) -> bool {
103
-        self.map_view.viewport_in_map()
110
+    pub fn map_covers_viewport(&self) -> bool {
111
+        match self.view_mode {
112
+            ViewMode::Flat => self.map_view.map_covers_viewport(),
113
+            ViewMode::Globe => self.map_view.globe_covers_viewport(),
114
+        }
104 115
     }
105 116
 
106 117
     pub fn increase_atlas_size(&mut self, cx: &mut Context) -> Result<(), ()> {
107 118
         self.tile_atlas.double_texture_size(cx)
108 119
     }
109 120
 
110
-    fn draw_tiles(&mut self, cx: &mut Context, source: &TileSource, snap_to_pixel: bool)
121
+    pub fn toggle_view_mode(&mut self) {
122
+        self.view_mode = match self.view_mode {
123
+            ViewMode::Flat => ViewMode::Globe,
124
+            ViewMode::Globe => ViewMode::Flat,
125
+        };
126
+    }
127
+
128
+    fn draw_flat_tiles(&mut self, cx: &mut Context, source: &TileSource, snap_to_pixel: bool)
111 129
         -> Result<usize, usize>
112 130
     {
113 131
         if self.last_draw_type != DrawType::Tiles {
@@ -115,6 +133,7 @@ impl MapViewGl {
115 133
             self.tile_layer.prepare_draw(cx, &self.tile_atlas);
116 134
         }
117 135
 
136
+        //TODO remove viewport_size parameter
118 137
         self.tile_layer.draw(
119 138
             cx,
120 139
             &self.map_view,
@@ -132,6 +151,7 @@ impl MapViewGl {
132 151
             self.marker_layer.prepare_draw(cx);
133 152
         }
134 153
 
154
+        //TODO remove viewport_size parameter
135 155
         self.marker_layer.draw(cx, &self.map_view, self.viewport_size, snap_to_pixel);
136 156
     }
137 157
 
@@ -147,7 +167,6 @@ impl MapViewGl {
147 167
             source,
148 168
             &mut self.tile_cache,
149 169
             &mut self.tile_atlas,
150
-            self.viewport_size,
151 170
         );
152 171
     }
153 172
 
@@ -158,15 +177,19 @@ impl MapViewGl {
158 177
         // only snap to pixel grid if zoom has integral value
159 178
         let snap_to_pixel = (self.map_view.zoom - (self.map_view.zoom + 0.5).floor()).abs() < 1e-10;
160 179
 
161
-        let ret = self.draw_tiles(cx, source, snap_to_pixel);
162
-
163
-        if !self.marker_layer.is_empty() {
164
-            self.draw_marker(cx, snap_to_pixel);
180
+        match self.view_mode {
181
+            ViewMode::Flat => {
182
+                let ret = self.draw_flat_tiles(cx, source, snap_to_pixel);
183
+                if !self.marker_layer.is_empty() {
184
+                    self.draw_marker(cx, snap_to_pixel);
185
+                }
186
+                ret
187
+            },
188
+            ViewMode::Globe => {
189
+                self.draw_globe(cx, source);
190
+                Ok(1)
191
+            },
165 192
         }
166
-
167
-        self.draw_globe(cx, source);
168
-
169
-        ret
170 193
     }
171 194
 
172 195
     pub fn step_zoom(&mut self, steps: i32, step_size: f64) {