pub mod tiles; pub mod osm; use std::io::Cursor; use eframe::glow; use image::{DynamicImage, ImageReader}; use poll_promise::Promise; use tracing::warn; use crate::map::osm::OSMBaselayer; use crate::map::tiles::{LayerManager, LayerSource, Tile}; pub struct Map { pub layer_manager: LayerManager, program: glow::Program, vertex_array: glow::VertexArray } #[allow(unsafe_code)] // we need unsafe code to use glow impl Map { pub fn new(layer_manager: LayerManager, gl: &glow::Context) -> Option { use glow::HasContext as _; let shader_version = egui_glow::ShaderVersion::get(gl); unsafe { let program = gl.create_program().expect("Cannot create program"); if !shader_version.is_new_shader_interface() { warn!( "Custom 3D painting hasn't been ported to {:?}", shader_version ); return None; } let (vertex_shader_source, fragment_shader_source) = ( r#" const vec2 verts[6] = vec2[6]( vec2(-1.0, 1.0), vec2(1.0, 1.0), vec2(-1.0, -1.0), vec2(-1.0, -1.0), vec2(1.0, 1.0), vec2(1.0, -1.0) ); const vec4 colors[6] = vec4[6]( vec4(1.0, 0.0, 0.0, 1.0), vec4(0.0, 1.0, 0.0, 1.0), vec4(0.0, 0.0, 1.0, 1.0), vec4(1.0, 0.0, 1.0, 1.0), vec4(1.0, 0.0, 0.0, 1.0), vec4(0.0, 1.0, 0.0, 1.0) ); out vec4 v_color; uniform float u_angle; void main() { v_color = colors[gl_VertexID]; gl_Position = vec4(verts[gl_VertexID], 0.0, 1.0); } "#, r#" precision mediump float; in vec4 v_color; out vec4 out_color; void main() { out_color = v_color; } "#, ); let shader_sources = [ (glow::VERTEX_SHADER, vertex_shader_source), (glow::FRAGMENT_SHADER, fragment_shader_source), ]; let shaders: Vec<_> = shader_sources .iter() .map(|(shader_type, shader_source)| { let shader = gl .create_shader(*shader_type) .expect("Cannot create shader"); gl.shader_source( shader, &format!( "{}\n{}", shader_version.version_declaration(), shader_source ), ); gl.compile_shader(shader); assert!( gl.get_shader_compile_status(shader), "Failed to compile custom_3d_glow {shader_type}: {}", gl.get_shader_info_log(shader) ); gl.attach_shader(program, shader); shader }) .collect(); gl.link_program(program); assert!( gl.get_program_link_status(program), "{}", gl.get_program_info_log(program) ); for shader in shaders { gl.detach_shader(program, shader); gl.delete_shader(shader); } let vertex_array = gl .create_vertex_array() .expect("Cannot create vertex array"); Some(Self { program, vertex_array, layer_manager }) } } pub fn destroy(&self, gl: &glow::Context) { use glow::HasContext as _; unsafe { gl.delete_program(self.program); gl.delete_vertex_array(self.vertex_array); } } pub fn update_view_data(&mut self, ctx: &egui::Context) { // test: download 0/0/0 if not already there let tile = self.layer_manager.tiles.entry((0, 0, 0, OSMBaselayer::SOURCE_ID)) .or_insert_with(|| { let (sender, promise) = Promise::new(); let request = ehttp::Request::get("https://tile.openstreetmap.org/0/0/0.png"); let ctx = ctx.clone(); ehttp::fetch(request, move | response | { sender.send(match response { Ok(r) => match ImageReader::new(Cursor::new(r.bytes)).with_guessed_format() { Ok(img) => match img.decode() { Ok(img) => { ctx.request_repaint(); // wake up the ui Ok(img) }, Err(e) => Err(e.to_string()), }, Err(e) => Err(e.to_string()), }, Err(e) => Err(e.to_string()), }); }); Tile { promise } }); } pub fn paint(&self, gl: &glow::Context, angle: f32) { use glow::HasContext as _; unsafe { gl.use_program(Some(self.program)); gl.uniform_1_f32( gl.get_uniform_location(self.program, "u_angle").as_ref(), angle, ); gl.bind_vertex_array(Some(self.vertex_array)); gl.draw_arrays(glow::TRIANGLES, 0, 6); } } }