Browse Source

Add path_layer

Draw paths with round joins, round caps and anti-aliasing.
Johannes Hofmann 7 years ago
parent
commit
8f71929195
6 changed files with 457 additions and 3 deletions
  1. 12
    0
      shader/path.frag
  2. 15
    0
      shader/path.vert
  3. 3
    1
      src/main.rs
  4. 28
    2
      src/map_view_gl.rs
  5. 384
    0
      src/path_layer.rs
  6. 15
    0
      src/program.rs

+ 12
- 0
shader/path.frag View File

@@ -0,0 +1,12 @@
1
+#version 100
2
+precision highp float;
3
+
4
+uniform float half_width;
5
+uniform vec3 color;
6
+
7
+varying vec2 v_extrusion;
8
+
9
+void main() {
10
+    float len = length(v_extrusion);
11
+    gl_FragColor = vec4(color, min(half_width - half_width * len, 1.0));
12
+}

+ 15
- 0
shader/path.vert View File

@@ -0,0 +1,15 @@
1
+#version 100
2
+precision highp float;
3
+
4
+attribute vec2 position;
5
+attribute vec2 extrusion;
6
+
7
+uniform vec2 scale;
8
+uniform float half_width;
9
+
10
+varying vec2 v_extrusion;
11
+
12
+void main() {
13
+    gl_Position = vec4(position + extrusion * scale * half_width, 0.0, 1.0);
14
+    v_extrusion = extrusion;
15
+}

+ 3
- 1
src/main.rs View File

@@ -33,6 +33,7 @@ pub mod mercator_tile_layer;
33 33
 pub mod mercator_view;
34 34
 pub mod ortho_tile_layer;
35 35
 pub mod orthografic_view;
36
+pub mod path_layer;
36 37
 pub mod program;
37 38
 pub mod projection;
38 39
 pub mod projection_view;
@@ -52,6 +53,7 @@ use coord::ScreenCoord;
52 53
 use glutin::dpi::{LogicalPosition, LogicalSize, PhysicalPosition};
53 54
 use glutin::{ControlFlow, ElementState, Event, GlContext, MouseButton, MouseScrollDelta, VirtualKeyCode, WindowEvent};
54 55
 use map_view_gl::MapViewGl;
56
+use path_layer::PathElement;
55 57
 use search::MatchItem;
56 58
 use std::collections::hash_set::HashSet;
57 59
 use std::error::Error;
@@ -101,7 +103,7 @@ fn handle_event(
101 103
             for item in marker_rx.try_iter().flat_map(|c| c.into_iter()) {
102 104
                 match item {
103 105
                     MatchItem::Node{pos, ..} => map.add_marker(pos.into()),
104
-                    MatchItem::Way{pos, ..} => map.add_marker(pos.into()),
106
+                    MatchItem::Way{pos, ..} => map.add_path_element(PathElement::MoveTo(pos.into())),
105 107
                 }
106 108
             }
107 109
             Action::Redraw

+ 28
- 2
src/map_view_gl.rs View File

@@ -7,6 +7,7 @@ use mercator_tile_layer::MercatorTileLayer;
7 7
 use mercator_view::MercatorView;
8 8
 use ortho_tile_layer::OrthoTileLayer;
9 9
 use orthografic_view::OrthograficView;
10
+use path_layer::{PathElement, PathLayer};
10 11
 use projection::Projection;
11 12
 use projection_view::ProjectionView;
12 13
 use session::Session;
@@ -29,6 +30,7 @@ pub struct MapViewGl {
29 30
     tile_atlas: TileAtlas,
30 31
     mercator_tile_layer: MercatorTileLayer,
31 32
     marker_layer: MarkerLayer,
33
+    path_layer: PathLayer,
32 34
     ortho_tile_layer: OrthoTileLayer,
33 35
     atmos_layer: AtmosLayer,
34 36
     show_marker: bool,
@@ -39,10 +41,11 @@ pub struct MapViewGl {
39 41
 #[derive(Debug, Eq, PartialEq)]
40 42
 enum DrawType {
41 43
     Null,
42
-    Tiles,
44
+    Atmos,
43 45
     Markers,
44 46
     OrthoTiles,
45
-    Atmos,
47
+    Path,
48
+    Tiles,
46 49
 }
47 50
 
48 51
 impl MapViewGl {
@@ -97,6 +100,7 @@ impl MapViewGl {
97 100
             tile_atlas,
98 101
             mercator_tile_layer,
99 102
             marker_layer: MarkerLayer::new(cx),
103
+            path_layer: PathLayer::new(cx),
100 104
             ortho_tile_layer,
101 105
             atmos_layer,
102 106
             show_marker: true,
@@ -123,6 +127,10 @@ impl MapViewGl {
123 127
         self.marker_layer.add_marker(map_coord);
124 128
     }
125 129
 
130
+    pub fn add_path_element(&mut self, ele: PathElement) {
131
+        self.path_layer.add_element(ele);
132
+    }
133
+
126 134
     pub fn map_covers_viewport(&self) -> bool {
127 135
         match &self.proj_view {
128 136
             ProjectionView::Mercator(ref merc) => merc.covers_viewport(),
@@ -169,6 +177,21 @@ impl MapViewGl {
169 177
         )
170 178
     }
171 179
 
180
+    fn draw_mercator_path(&mut self, cx: &mut Context, merc: &MercatorView, snap_to_pixel: bool)
181
+    {
182
+        if self.last_draw_type != DrawType::Path {
183
+            self.last_draw_type = DrawType::Path;
184
+            self.path_layer.prepare_draw(cx);
185
+        }
186
+
187
+        self.path_layer.draw_mercator(
188
+            cx,
189
+            merc,
190
+            self.dpi_factor,
191
+            snap_to_pixel
192
+        )
193
+    }
194
+
172 195
     fn draw_mercator_marker(&mut self, cx: &mut Context, merc: &MercatorView, snap_to_pixel: bool) {
173 196
         if self.last_draw_type != DrawType::Markers {
174 197
             self.last_draw_type = DrawType::Markers;
@@ -233,6 +256,9 @@ impl MapViewGl {
233 256
                 let snap_to_pixel = (merc.zoom - (merc.zoom + 0.5).floor()).abs() < 1e-10;
234 257
 
235 258
                 let ret = self.draw_mercator_tiles(cx, merc, source, snap_to_pixel);
259
+
260
+                self.draw_mercator_path(cx, merc, snap_to_pixel);
261
+
236 262
                 if self.show_marker && !self.marker_layer.is_empty() {
237 263
                     self.draw_mercator_marker(cx, merc, snap_to_pixel);
238 264
                 }

+ 384
- 0
src/path_layer.rs View File

@@ -0,0 +1,384 @@
1
+use ::std::ffi::CStr;
2
+use buffer::{Buffer, DrawMode};
3
+use cgmath::{InnerSpace, Matrix3, Point2, Transform, Vector2, vec3};
4
+use context::Context;
5
+use coord::{MapCoord};
6
+use mercator_view::MercatorView;
7
+use orthografic_view::OrthograficView;
8
+use program::{Program, UniformId};
9
+use vertex_attrib::VertexAttribParams;
10
+
11
+
12
+#[derive(Clone, Copy, Debug)]
13
+pub enum PathElement {
14
+    MoveTo(MapCoord),
15
+    LineTo(MapCoord),
16
+    ClosePath,
17
+}
18
+
19
+#[derive(Debug)]
20
+pub struct PathLayer {
21
+    buffer: Buffer,
22
+    program: Program,
23
+    scale_uniform: UniformId,
24
+    half_width_uniform: UniformId,
25
+    color_uniform: UniformId,
26
+    path: Vec<PathElement>,
27
+}
28
+
29
+impl PathLayer {
30
+    pub fn new(cx: &mut Context) -> PathLayer {
31
+        let buffer = Buffer::new(cx, &[], 0);
32
+        cx.bind_buffer(buffer.id());
33
+        check_gl_errors!(cx);
34
+
35
+        let mut program = Program::new(
36
+            cx,
37
+            include_bytes!("../shader/path.vert"),
38
+            include_bytes!("../shader/path.frag"),
39
+        ).unwrap();
40
+
41
+        program.add_attribute(
42
+            cx,
43
+            CStr::from_bytes_with_nul(b"position\0").unwrap(),
44
+            &VertexAttribParams::new(2, 4, 0)
45
+        );
46
+        program.add_attribute(
47
+            cx,
48
+            CStr::from_bytes_with_nul(b"extrusion\0").unwrap(),
49
+            &VertexAttribParams::new(2, 4, 2)
50
+        );
51
+
52
+        let scale_uniform = program.get_uniform_id(cx, CStr::from_bytes_with_nul(b"scale\0").unwrap()).unwrap();
53
+        let half_width_uniform = program.get_uniform_id(cx, CStr::from_bytes_with_nul(b"half_width\0").unwrap()).unwrap();
54
+        let color_uniform = program.get_uniform_id(cx, CStr::from_bytes_with_nul(b"color\0").unwrap()).unwrap();
55
+
56
+        PathLayer {
57
+            buffer,
58
+            program,
59
+            scale_uniform,
60
+            half_width_uniform,
61
+            color_uniform,
62
+            path: vec![],
63
+        }
64
+    }
65
+
66
+    pub fn is_empty(&self) -> bool {
67
+        self.path.is_empty()
68
+    }
69
+
70
+    pub fn add_element(&mut self, ele: PathElement) {
71
+        self.path.push(ele);
72
+    }
73
+
74
+    pub fn line_to(&mut self, map_coord: MapCoord) {
75
+        self.path.push(PathElement::LineTo(map_coord));
76
+    }
77
+
78
+    pub fn move_to(&mut self, map_coord: MapCoord) {
79
+        self.path.push(PathElement::MoveTo(map_coord));
80
+    }
81
+
82
+    pub fn close_path(&mut self) {
83
+        self.path.push(PathElement::ClosePath);
84
+    }
85
+
86
+    // Has to be called once before one or multiple calls to `draw`.
87
+    pub fn prepare_draw(&mut self, cx: &mut Context) {
88
+        self.program.enable_vertex_attribs(cx);
89
+        self.program.set_vertex_attribs(cx, &self.buffer);
90
+    }
91
+
92
+    #[inline]
93
+    fn vec_ortho(v: Vector2<f32>) -> Vector2<f32> {
94
+        Vector2::new(v.y, -v.x)
95
+    }
96
+
97
+    fn add_line_join(
98
+        point_a: Point2<f32>,
99
+        normal_a: Vector2<f32>,
100
+        normal_b: Vector2<f32>,
101
+        vertex_data: &mut Vec<f32>
102
+    ) {
103
+        let dot = normal_b.dot(normal_a);
104
+        let perp_dot = normal_b.perp_dot(normal_a);
105
+
106
+        if dot >= 0.0 {
107
+            // angle between segments is 90° or more
108
+
109
+            let extrusion_normal = (normal_a + normal_b).normalize();
110
+            let extrusion = extrusion_normal / normal_a.dot(extrusion_normal);
111
+
112
+            if perp_dot > 0.0 {
113
+                vertex_data.extend::<&[f32; 2]>(point_a.as_ref());
114
+                vertex_data.extend::<&[f32; 2]>((-normal_b).as_ref());
115
+                vertex_data.extend::<&[f32; 2]>(point_a.as_ref());
116
+                vertex_data.extend::<&[f32; 2]>((-extrusion).as_ref());
117
+                vertex_data.extend::<&[f32; 2]>(point_a.as_ref());
118
+                vertex_data.extend::<&[f32; 2]>((-extrusion).as_ref());
119
+            } else if perp_dot < 0.0 {
120
+                vertex_data.extend::<&[f32; 2]>(point_a.as_ref());
121
+                vertex_data.extend::<&[f32; 2]>(extrusion.as_ref());
122
+                vertex_data.extend::<&[f32; 2]>(point_a.as_ref());
123
+                vertex_data.extend::<&[f32; 2]>(normal_b.as_ref());
124
+                vertex_data.extend::<&[f32; 2]>(point_a.as_ref());
125
+                vertex_data.extend::<&[f32; 2]>(normal_b.as_ref());
126
+            }
127
+        } else {
128
+            // angle between segments is less than 90°
129
+
130
+            let tangent_a = -Self::vec_ortho(normal_a);
131
+            let tangent_b = Self::vec_ortho(normal_b);
132
+
133
+            if perp_dot > 0.0 {
134
+                vertex_data.extend::<&[f32; 2]>(point_a.as_ref());
135
+                vertex_data.extend::<&[f32; 2]>((-normal_b).as_ref());
136
+                vertex_data.extend::<&[f32; 2]>(point_a.as_ref());
137
+                vertex_data.extend::<&[f32; 2]>((-normal_a + tangent_a).as_ref());
138
+                vertex_data.extend::<&[f32; 2]>(point_a.as_ref());
139
+                vertex_data.extend::<&[f32; 2]>((-normal_b + tangent_b).as_ref());
140
+                vertex_data.extend::<&[f32; 2]>(point_a.as_ref());
141
+                vertex_data.extend::<&[f32; 2]>((-normal_b + tangent_b).as_ref());
142
+            } else if perp_dot < 0.0 {
143
+                vertex_data.extend::<&[f32; 2]>(point_a.as_ref());
144
+                vertex_data.extend::<&[f32; 2]>((normal_a + tangent_a).as_ref());
145
+                vertex_data.extend::<&[f32; 2]>(point_a.as_ref());
146
+                vertex_data.extend::<&[f32; 2]>(normal_b.as_ref());
147
+                vertex_data.extend::<&[f32; 2]>(point_a.as_ref());
148
+                vertex_data.extend::<&[f32; 2]>((normal_b + tangent_b).as_ref());
149
+                vertex_data.extend::<&[f32; 2]>(point_a.as_ref());
150
+                vertex_data.extend::<&[f32; 2]>((normal_b + tangent_b).as_ref());
151
+            }
152
+        }
153
+    }
154
+
155
+    fn add_line_segment(
156
+        point_a: Point2<f32>,
157
+        point_b: Point2<f32>,
158
+        normal: Vector2<f32>,
159
+        vertex_data: &mut Vec<f32>
160
+    ) {
161
+        vertex_data.extend::<&[f32; 2]>(point_a.as_ref());
162
+        vertex_data.extend::<&[f32; 2]>(normal.as_ref());
163
+        vertex_data.extend::<&[f32; 2]>(point_a.as_ref());
164
+        vertex_data.extend::<&[f32; 2]>(normal.as_ref());
165
+        vertex_data.extend::<&[f32; 2]>(point_a.as_ref());
166
+        vertex_data.extend::<&[f32; 2]>((-normal).as_ref());
167
+        vertex_data.extend::<&[f32; 2]>(point_b.as_ref());
168
+        vertex_data.extend::<&[f32; 2]>(normal.as_ref());
169
+        vertex_data.extend::<&[f32; 2]>(point_b.as_ref());
170
+        vertex_data.extend::<&[f32; 2]>((-normal).as_ref());
171
+    }
172
+
173
+    fn add_double_cap(
174
+        point: Point2<f32>,
175
+        vertex_data: &mut Vec<f32>
176
+    ) {
177
+        let normal = Vector2::new(1.0, 0.0);
178
+        let tangent = -Self::vec_ortho(normal);
179
+        vertex_data.extend::<&[f32; 2]>(point.as_ref());
180
+        vertex_data.extend::<&[f32; 2]>((normal + tangent).as_ref());
181
+        vertex_data.extend::<&[f32; 2]>(point.as_ref());
182
+        vertex_data.extend::<&[f32; 2]>((normal + tangent).as_ref());
183
+        vertex_data.extend::<&[f32; 2]>(point.as_ref());
184
+        vertex_data.extend::<&[f32; 2]>((-normal + tangent).as_ref());
185
+        vertex_data.extend::<&[f32; 2]>(point.as_ref());
186
+        vertex_data.extend::<&[f32; 2]>((normal - tangent).as_ref());
187
+        vertex_data.extend::<&[f32; 2]>(point.as_ref());
188
+        vertex_data.extend::<&[f32; 2]>((-normal - tangent).as_ref());
189
+        vertex_data.extend::<&[f32; 2]>(point.as_ref());
190
+        vertex_data.extend::<&[f32; 2]>((-normal - tangent).as_ref());
191
+    }
192
+
193
+    fn add_cap(
194
+        point: Point2<f32>,
195
+        normal: Vector2<f32>,
196
+        vertex_data: &mut Vec<f32>
197
+    ) {
198
+        let tangent = -Self::vec_ortho(normal);
199
+        vertex_data.extend::<&[f32; 2]>(point.as_ref());
200
+        vertex_data.extend::<&[f32; 2]>((normal + tangent).as_ref());
201
+        vertex_data.extend::<&[f32; 2]>(point.as_ref());
202
+        vertex_data.extend::<&[f32; 2]>((-normal + tangent).as_ref());
203
+        vertex_data.extend::<&[f32; 2]>(point.as_ref());
204
+        vertex_data.extend::<&[f32; 2]>((-normal + tangent).as_ref());
205
+    }
206
+
207
+    fn add_separated_cap(
208
+        point: Point2<f32>,
209
+        normal: Vector2<f32>,
210
+        vertex_data: &mut Vec<f32>
211
+    ) {
212
+        let tangent = Self::vec_ortho(normal);
213
+        vertex_data.extend::<&[f32; 2]>(point.as_ref());
214
+        vertex_data.extend::<&[f32; 2]>(normal.as_ref());
215
+        vertex_data.extend::<&[f32; 2]>(point.as_ref());
216
+        vertex_data.extend::<&[f32; 2]>(normal.as_ref());
217
+        vertex_data.extend::<&[f32; 2]>(point.as_ref());
218
+        vertex_data.extend::<&[f32; 2]>((-normal).as_ref());
219
+        vertex_data.extend::<&[f32; 2]>(point.as_ref());
220
+        vertex_data.extend::<&[f32; 2]>((normal + tangent).as_ref());
221
+        vertex_data.extend::<&[f32; 2]>(point.as_ref());
222
+        vertex_data.extend::<&[f32; 2]>((-normal + tangent).as_ref());
223
+        vertex_data.extend::<&[f32; 2]>(point.as_ref());
224
+        vertex_data.extend::<&[f32; 2]>((-normal + tangent).as_ref());
225
+    }
226
+
227
+    fn add_caps<F> (
228
+        current_point: Option<Point2<f32>>,
229
+        current_normal: Option<Vector2<f32>>,
230
+        start_point: Option<Point2<f32>>,
231
+        start_normal: Option<Vector2<f32>>,
232
+        pixel_to_screen: F,
233
+        vertex_data: &mut Vec<f32>,
234
+    )
235
+        where F: Fn(Point2<f32>) -> Point2<f32>,
236
+    {
237
+        if let Some(point_a) = current_point {
238
+            let point_a: Point2<f32> = pixel_to_screen(point_a);
239
+
240
+            if let Some(normal) = current_normal {
241
+                Self::add_cap(point_a, normal, vertex_data);
242
+
243
+                if let (Some(point), Some(normal)) = (start_point, start_normal) {
244
+                    let point: Point2<f32> = pixel_to_screen(point);
245
+                    Self::add_separated_cap(point, normal, vertex_data);
246
+                }
247
+            } else {
248
+                Self::add_double_cap(point_a, vertex_data);
249
+            }
250
+        }
251
+    }
252
+
253
+    pub fn draw_mercator(
254
+        &mut self,
255
+        cx: &mut Context,
256
+        merc: &MercatorView,
257
+        dpi_factor: f64,
258
+        snap_to_pixel: bool
259
+    ) {
260
+        let mut vertex_data: Vec<f32> = vec![];
261
+
262
+        let scale_x = 2.0 / merc.viewport_size.x as f32;
263
+        let scale_y = -2.0 / merc.viewport_size.y as f32;
264
+
265
+        let half_width = 4.0 * dpi_factor as f32;
266
+
267
+        let screen_mat: Matrix3<f32> = Matrix3::from_cols(
268
+            vec3(scale_x, 0.0, 0.0),
269
+            vec3(0.0, scale_y, 0.0),
270
+            vec3(-1.0, 1.0, 1.0),
271
+        );
272
+
273
+        let map_to_screen = |mc: MapCoord| -> Point2<f32> {
274
+            let mut sp = merc.map_to_screen_coord(mc);
275
+            if snap_to_pixel {
276
+                let topleft = merc.map_to_screen_coord(MapCoord::new(0.0, 0.0));
277
+                let mut snapped = topleft;
278
+                snapped.snap_to_pixel();
279
+
280
+                sp.x += snapped.x - topleft.x;
281
+                sp.y += snapped.y - topleft.y;
282
+            }
283
+            Point2::new(sp.x as f32, sp.y as f32)
284
+        };
285
+
286
+        let mut current_point: Option<Point2<f32>> = None;
287
+        let mut current_normal: Option<Vector2<f32>> = None;
288
+        let mut start_point: Option<Point2<f32>> = None;
289
+        let mut start_normal: Option<Vector2<f32>> = None;
290
+
291
+        for element in &self.path {
292
+            match element {
293
+                PathElement::MoveTo(mc) => {
294
+                    Self::add_caps(
295
+                        current_point,
296
+                        current_normal,
297
+                        start_point,
298
+                        start_normal,
299
+                        |p| screen_mat.transform_point(p),
300
+                        &mut vertex_data,
301
+                    );
302
+
303
+                    current_point = Some(map_to_screen(*mc));
304
+                    current_normal = None;
305
+                    start_point = current_point;
306
+                    start_normal = None;
307
+                },
308
+                PathElement::LineTo(mc) => {
309
+                    let point_b = map_to_screen(*mc);
310
+                    if let Some(point_a) = current_point {
311
+                        let normal_b = Self::vec_ortho(point_b - point_a).normalize();
312
+                        let point_a: Point2<f32> = screen_mat.transform_point(point_a);
313
+                        let point_b: Point2<f32> = screen_mat.transform_point(point_b);
314
+
315
+                        if let Some(normal_a) = current_normal {
316
+                            Self::add_line_join(point_a, normal_a, normal_b, &mut vertex_data);
317
+                        }
318
+
319
+                        Self::add_line_segment(point_a, point_b, normal_b, &mut vertex_data);
320
+
321
+                        current_normal = Some(normal_b);
322
+                        start_normal = start_normal.or(Some(normal_b));
323
+                    } else {
324
+                        current_normal = None;
325
+                        start_normal = None;
326
+                    }
327
+                    current_point = Some(point_b);
328
+                    start_point = start_point.or(Some(point_b));
329
+                },
330
+                PathElement::ClosePath => {
331
+                    if let Some(point_a) = current_point {
332
+                        if let (Some(normal_a), Some(point_b), Some(normal_c)) = (current_normal, start_point, start_normal) {
333
+                            let normal_b = Self::vec_ortho(point_b - point_a).normalize();
334
+                            let point_a: Point2<f32> = screen_mat.transform_point(point_a);
335
+                            let point_b: Point2<f32> = screen_mat.transform_point(point_b);
336
+
337
+                            Self::add_line_join(point_a, normal_a, normal_b, &mut vertex_data);
338
+                            Self::add_line_segment(point_a, point_b, normal_b, &mut vertex_data);
339
+                            Self::add_line_join(point_b, normal_b, normal_c, &mut vertex_data);
340
+                        } else {
341
+                            let point_a: Point2<f32> = screen_mat.transform_point(point_a);
342
+                            Self::add_double_cap(point_a, &mut vertex_data);
343
+                        }
344
+                    }
345
+
346
+                    current_point = None;
347
+                    current_normal = None;
348
+                    start_point = None;
349
+                    start_normal = None;
350
+                },
351
+            }
352
+        }
353
+
354
+        Self::add_caps(
355
+            current_point,
356
+            current_normal,
357
+            start_point,
358
+            start_normal,
359
+            |p| screen_mat.transform_point(p),
360
+            &mut vertex_data,
361
+        );
362
+
363
+        self.buffer.set_data(cx, &vertex_data, vertex_data.len() / 4);
364
+
365
+        self.program.set_uniform_2f(cx, self.scale_uniform, scale_x, scale_y);
366
+        self.program.set_uniform_1f(cx, self.half_width_uniform, half_width);
367
+        self.program.set_uniform_3f(cx, self.color_uniform, 0.0, 0.0, 0.0);
368
+        self.buffer.draw(cx, &self.program, DrawMode::TriangleStrip);
369
+
370
+        self.program.set_uniform_1f(cx, self.half_width_uniform, half_width / 3.0);
371
+        self.program.set_uniform_3f(cx, self.color_uniform, 1.0, 1.0, 1.0);
372
+        self.buffer.draw(cx, &self.program, DrawMode::TriangleStrip);
373
+    }
374
+
375
+
376
+    pub fn draw_ortho(
377
+        &mut self,
378
+        cx: &mut Context,
379
+        ortho: &OrthograficView,
380
+        dpi_factor: f64,
381
+    ) {
382
+        //TODO implement
383
+    }
384
+}

+ 15
- 0
src/program.rs View File

@@ -155,6 +155,14 @@ impl Program {
155 155
         }
156 156
     }
157 157
 
158
+    //TODO implement as generic function
159
+    pub fn set_uniform_1f(&mut self, cx: &mut Context, uniform_id: UniformId, v0: f32) {
160
+        cx.use_program(self.program_id);
161
+        unsafe {
162
+            cx.gl.Uniform1f(uniform_id.id, v0);
163
+        };
164
+    }
165
+
158 166
     pub fn set_uniform_2f(&mut self, cx: &mut Context, uniform_id: UniformId, v0: f32, v1: f32) {
159 167
         cx.use_program(self.program_id);
160 168
         unsafe {
@@ -162,6 +170,13 @@ impl Program {
162 170
         };
163 171
     }
164 172
 
173
+    pub fn set_uniform_3f(&mut self, cx: &mut Context, uniform_id: UniformId, v0: f32, v1: f32, v2: f32) {
174
+        cx.use_program(self.program_id);
175
+        unsafe {
176
+            cx.gl.Uniform3f(uniform_id.id, v0, v1, v2);
177
+        };
178
+    }
179
+
165 180
     //TODO rename function or integrate into new()
166 181
     pub fn add_attribute(
167 182
         &mut self,