use wgpu; use bytemuck::{Pod, Zeroable}; use anyhow::Result; /// Vertex format for road geometry (matches frontend RoadVertex) #[repr(C)] #[derive(Copy, Clone, Debug, Pod, Zeroable)] pub struct RoadVertex { pub center: [f32; 2], pub normal: [f32; 2], pub lanes: f32, pub road_type: f32, } /// Vertex format for colored buildings (matches frontend ColoredVertex) #[repr(C)] #[derive(Copy, Clone, Debug, Pod, Zeroable)] pub struct ColoredVertex { pub position: [f32; 2], pub color: [f32; 3], pub _padding: f32, // Align to 16 bytes } /// Vertex format for simple polygons (matches frontend Vertex) #[repr(C)] #[derive(Copy, Clone, Debug, Pod, Zeroable)] pub struct SimpleVertex { pub position: [f32; 2], } /// Vertex format for railways (matches frontend RailwayVertex) #[repr(C)] #[derive(Copy, Clone, Debug, Pod, Zeroable)] pub struct RailwayVertex { pub center: [f32; 2], pub normal: [f32; 2], pub color: [f32; 3], pub rail_type: f32, } /// GPU-based mesh generation service /// Uses compute shaders to precompute geometry on the server /// Falls back to CPU-only if GPU is not available pub struct MeshGenerationService { device: Option, queue: Option, } impl MeshGenerationService { /// Initialize GPU device for headless compute /// Falls back to CPU-only if GPU is unavailable pub async fn new() -> Result { let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { backends: wgpu::Backends::all(), ..Default::default() }); let adapter = instance .request_adapter(&wgpu::RequestAdapterOptions { power_preference: wgpu::PowerPreference::HighPerformance, force_fallback_adapter: false, compatible_surface: None, }) .await; match adapter { Some(adapter) => { match adapter.request_device( &wgpu::DeviceDescriptor { label: Some("Mesh Generation Device"), required_features: wgpu::Features::empty(), required_limits: wgpu::Limits::default(), }, None, ).await { Ok((device, queue)) => { println!("GPU initialized: {:?}", adapter.get_info()); Ok(Self { device: Some(device), queue: Some(queue) }) } Err(e) => { println!("GPU device request failed: {}, falling back to CPU-only mode", e); Ok(Self { device: None, queue: None }) } } } None => { println!("No GPU adapter found, using CPU-only mesh generation"); Ok(Self { device: None, queue: None }) } } } /// Generate road geometry with miter joins (CPU implementation for now) /// Returns vertex buffer as raw bytes ready for storage pub fn generate_road_geometry( &self, points: &[[f32; 2]], lanes: f32, road_type: f32, ) -> Vec { let vertices = Self::generate_road_mesh(points, lanes, road_type); bytemuck::cast_slice(&vertices).to_vec() } /// Generate building geometry (already triangulated) pub fn generate_building_geometry( &self, points: &[[f32; 2]], color: [f32; 3], ) -> Vec { let vertices: Vec = points .iter() .map(|&position| ColoredVertex { position, color, _padding: 0.0, }) .collect(); bytemuck::cast_slice(&vertices).to_vec() } /// Generate simple polygon geometry (landuse, water) pub fn generate_polygon_geometry(&self, points: &[[f32; 2]]) -> Vec { let vertices: Vec = points .iter() .map(|&position| SimpleVertex { position }) .collect(); bytemuck::cast_slice(&vertices).to_vec() } /// Generate railway geometry with color pub fn generate_railway_geometry( &self, points: &[[f32; 2]], color: [f32; 3], rail_type: f32, ) -> Vec { let vertices = Self::generate_railway_mesh(points, color, rail_type); bytemuck::cast_slice(&vertices).to_vec() } // ======================================================================== // CPU-based mesh generation (port from frontend) // TODO: Replace with GPU compute shaders for better performance // ======================================================================== fn normalize(v: [f32; 2]) -> [f32; 2] { let len = (v[0] * v[0] + v[1] * v[1]).sqrt(); if len < 0.00001 { [0.0, 0.0] } else { [v[0] / len, v[1] / len] } } fn dot(a: [f32; 2], b: [f32; 2]) -> f32 { a[0] * b[0] + a[1] * b[1] } fn generate_road_mesh(points: &[[f32; 2]], lanes: f32, road_type: f32) -> Vec { let debug_mesh = std::env::var("DEBUG_MESH").is_ok(); if points.len() < 2 { if debug_mesh { println!("DEBUG MESH: Too few points ({})", points.len()); } return Vec::new(); } // Compute normals for each segment let mut segment_normals = Vec::with_capacity(points.len() - 1); let mut degenerate_count = 0; for i in 0..points.len() - 1 { let p1 = points[i]; let p2 = points[i + 1]; let dx = p2[0] - p1[0]; let dy = p2[1] - p1[1]; let len = (dx * dx + dy * dy).sqrt(); if len < 0.000001 { segment_normals.push([0.0, 0.0]); degenerate_count += 1; } else { segment_normals.push([-dy / len, dx / len]); } } if debug_mesh && degenerate_count > 0 { println!("DEBUG MESH: {}/{} segments degenerate", degenerate_count, segment_normals.len()); } // Generate vertex pairs with miter joins let mut point_pairs = Vec::with_capacity(points.len() * 2); for i in 0..points.len() { let normal: [f32; 2]; let mut miter_len = 1.0f32; if i == 0 { normal = segment_normals[0]; } else if i == points.len() - 1 { normal = segment_normals[i - 1]; } else { let n1 = segment_normals[i - 1]; let n2 = segment_normals[i]; if Self::dot(n1, n1) == 0.0 { normal = n2; } else if Self::dot(n2, n2) == 0.0 { normal = n1; } else { let sum = [n1[0] + n2[0], n1[1] + n2[1]]; let miter = Self::normalize(sum); let d = Self::dot(miter, n1); if d.abs() < 0.1 { normal = n1; } else { miter_len = 1.0 / d; if miter_len > 4.0 { miter_len = 4.0; } normal = miter; } } } let p = points[i]; let nx = normal[0] * miter_len; let ny = normal[1] * miter_len; point_pairs.push(RoadVertex { center: p, normal: [nx, ny], lanes, road_type, }); point_pairs.push(RoadVertex { center: p, normal: [-nx, -ny], lanes, road_type, }); } // Triangulate let mut triangle_vertices = Vec::with_capacity((points.len() - 1) * 6); let mut skipped = 0; for i in 0..points.len() - 1 { if Self::dot(segment_normals[i], segment_normals[i]) == 0.0 { skipped += 1; continue; } let i_base = 2 * i; let j_base = 2 * (i + 1); let v1 = point_pairs[i_base]; let v2 = point_pairs[i_base + 1]; let v3 = point_pairs[j_base]; let v4 = point_pairs[j_base + 1]; triangle_vertices.push(v1); triangle_vertices.push(v2); triangle_vertices.push(v3); triangle_vertices.push(v2); triangle_vertices.push(v4); triangle_vertices.push(v3); } if debug_mesh { println!("DEBUG MESH: Generated {} vertices from {} points (skipped {} segments)", triangle_vertices.len(), points.len(), skipped); } triangle_vertices } fn generate_railway_mesh( points: &[[f32; 2]], color: [f32; 3], rail_type: f32, ) -> Vec { if points.len() < 2 { return Vec::new(); } // Similar to road mesh but for railways let mut segment_normals = Vec::with_capacity(points.len() - 1); for i in 0..points.len() - 1 { let p1 = points[i]; let p2 = points[i + 1]; let dx = p2[0] - p1[0]; let dy = p2[1] - p1[1]; let len = (dx * dx + dy * dy).sqrt(); if len < 0.000001 { segment_normals.push([0.0, 0.0]); } else { segment_normals.push([-dy / len, dx / len]); } } let mut point_pairs = Vec::with_capacity(points.len() * 2); for i in 0..points.len() { let normal = if i == 0 { segment_normals[0] } else if i == points.len() - 1 { segment_normals[i - 1] } else { let n1 = segment_normals[i - 1]; let n2 = segment_normals[i]; let sum = [n1[0] + n2[0], n1[1] + n2[1]]; Self::normalize(sum) }; let p = points[i]; point_pairs.push(RailwayVertex { center: p, normal, color, rail_type, }); point_pairs.push(RailwayVertex { center: p, normal: [-normal[0], -normal[1]], color, rail_type, }); } let mut triangle_vertices = Vec::with_capacity((points.len() - 1) * 6); for i in 0..points.len() - 1 { if Self::dot(segment_normals[i], segment_normals[i]) == 0.0 { continue; } let i_base = 2 * i; let j_base = 2 * (i + 1); let v1 = point_pairs[i_base]; let v2 = point_pairs[i_base + 1]; let v3 = point_pairs[j_base]; let v4 = point_pairs[j_base + 1]; triangle_vertices.push(v1); triangle_vertices.push(v2); triangle_vertices.push(v3); triangle_vertices.push(v2); triangle_vertices.push(v4); triangle_vertices.push(v3); } triangle_vertices } }