From 0e17c41deb181a28be65b0b94d7f2da94620eb13 Mon Sep 17 00:00:00 2001 From: Paris DOUADY Date: Wed, 3 Jan 2024 15:29:42 +0100 Subject: [PATCH] heightfinder on interfaced points + fix visual artefacts --- native_app/src/gui/roadbuild.rs | 33 ++++----- .../src/rendering/map_rendering/map_mesh.rs | 23 ++++--- native_app/src/rendering/map_rendering/mod.rs | 2 +- simulation/src/map/map.rs | 3 +- simulation/src/map/objects/road.rs | 68 ++++++++++--------- 5 files changed, 68 insertions(+), 61 deletions(-) diff --git a/native_app/src/gui/roadbuild.rs b/native_app/src/gui/roadbuild.rs index 8f78ba60..b089f998 100644 --- a/native_app/src/gui/roadbuild.rs +++ b/native_app/src/gui/roadbuild.rs @@ -92,7 +92,11 @@ pub fn roadbuild(sim: &Simulation, uiworld: &mut UiWorld) { if let proj @ MapProject { kind: Inter(_), .. } = map.project(to.pos, 0.0, ProjectFilter::ALL) { - state.build_state = Start(proj); + if matches!(tool, Tool::RoadbuildCurved) { + state.build_state = StartInterp(proj); + } else { + state.build_state = Start(proj); + } } } } @@ -222,23 +226,17 @@ pub fn roadbuild(sim: &Simulation, uiworld: &mut UiWorld) { None => RoadSegmentKind::Straight, }; - points = match simulation::map::Road::generate_points( + let (p, err) = simulation::map::Road::generate_points( selected_proj.pos, cur_proj.pos, connection_segment, is_rail, &map.environment, - ) { - Ok(p) => Some(p), - Err((p, _err)) => { - is_valid = false; - if let Some(p) = p { - Some(p) - } else { - None - } - } - }; + ); + points = Some(p); + if err.is_some() { + is_valid = false; + } } state.update_drawing(map, immdraw, cur_proj, patwidth, is_valid, points); @@ -315,7 +313,9 @@ fn check_angle(map: &Map, from: MapProject, to: Vec2, is_rail: bool) -> bool { true } Road(r) => { - let r = &map.roads()[r]; // fixme don't crash + let Some(r) = map.roads().get(r) else { + return false; + }; let (proj, _, rdir1) = r.points().project_segment_dir(from.pos); let rdir2 = -rdir1; let dir = (to - proj.xy()).normalize(); @@ -331,10 +331,7 @@ fn check_angle(map: &Map, from: MapProject, to: Vec2, is_rail: bool) -> bool { } fn compatible(map: &Map, x: MapProject, y: MapProject) -> bool { - // enforce at most 18 deg angle - if x.pos.distance(y.pos) < 10.0 - || (x.pos.z - y.pos.z).abs() > 0.2 * x.pos.xy().distance(y.pos.xy()) - { + if x.pos.distance(y.pos) < 10.0 { return false; } match (x.kind, y.kind) { diff --git a/native_app/src/rendering/map_rendering/map_mesh.rs b/native_app/src/rendering/map_rendering/map_mesh.rs index cf4606dd..62c129ed 100644 --- a/native_app/src/rendering/map_rendering/map_mesh.rs +++ b/native_app/src/rendering/map_rendering/map_mesh.rs @@ -11,7 +11,7 @@ use geom::{minmax, vec2, vec3, Color, LinearColor, PolyLine3, Polygon, Radians, use simulation::map::{ Building, BuildingKind, CanonicalPosition, Environment, Intersection, LaneKind, Lanes, LotKind, Map, MapSubscriber, ProjectFilter, ProjectKind, PylonPosition, Road, Roads, SubscriberChunkID, - Turn, TurnKind, UpdateType, CROSSWALK_WIDTH, + Turn, TurnKind, UpdateType, CROSSWALK_WIDTH, ROAD_Z_OFFSET, }; use simulation::souls::goods_company::GoodsCompanyRegistry; use simulation::Simulation; @@ -665,12 +665,14 @@ impl MapBuilders { for inter in chunk_inters { let inter = &inters[inter]; + let interpos = inter.pos.up(ROAD_Z_OFFSET); + if inter.roads.is_empty() { self.tess_map.set_color(line_col); - self.tess_map.draw_circle(inter.pos, 5.5); + self.tess_map.draw_circle(interpos, 5.5); self.tess_map.set_color(mid_col); - self.tess_map.draw_circle(inter.pos, 5.0); + self.tess_map.draw_circle(interpos, 5.0); continue; } @@ -756,7 +758,7 @@ fn add_polyon( let color: [f32; 4] = color.into(); let up = pos.up(-0.2); - let down = pos.xy().z(terrain_height); + let down = pos.xy().z(terrain_height - 20.0); let dirp = dir.perp_up(); let d2 = dir.xy().z0(); let d2p = d2.perp_up(); @@ -833,8 +835,10 @@ fn inter_pylon( inter: &Intersection, roads: &Roads, ) { + let interpos = inter.pos.up(ROAD_Z_OFFSET); + let h = unwrap_ret!(env.height(inter.pos.xy())); - if (h - inter.pos.z).abs() <= 2.0 { + if (h - interpos.z).abs() <= 2.0 { return; } @@ -849,7 +853,7 @@ fn inter_pylon( if !inter.roads.is_empty() { avgp /= inter.roads.len() as f32; } else { - avgp = inter.pos; + avgp = interpos; } add_polyon( @@ -869,6 +873,7 @@ fn intersection_mesh( inter: &Intersection, roads: &Roads, ) { + let interpos = inter.pos.up(ROAD_Z_OFFSET); let id = inter.id; let getw = |road: &Road| { @@ -921,7 +926,7 @@ fn intersection_mesh( if inter.is_roundabout() { if let Some(rp) = inter.turn_policy.roundabout { - let center = inter.pos.xy(); + let center = interpos.xy(); let ang = (left - center) .normalize() @@ -938,7 +943,7 @@ fn intersection_mesh( )); tess.set_color(center_col); - tess.draw_circle(center.z(inter.pos.z + 0.01), rp.radius * 0.5); + tess.draw_circle(center.z(interpos.z + 0.01), rp.radius * 0.5); continue; } @@ -956,7 +961,7 @@ fn intersection_mesh( tess.meshbuilder .extend_with(None, move |vertices, add_idx| { vertices.extend(polygon.iter().map(|pos| MeshVertex { - position: pos.z(inter.pos.z - 0.001).into(), + position: pos.z(interpos.z - 0.001).into(), normal: Vec3::Z, uv: [0.0; 2], color: col, diff --git a/native_app/src/rendering/map_rendering/mod.rs b/native_app/src/rendering/map_rendering/mod.rs index 9b6326cb..76d0838c 100644 --- a/native_app/src/rendering/map_rendering/mod.rs +++ b/native_app/src/rendering/map_rendering/mod.rs @@ -145,7 +145,7 @@ impl MapRenderer { 0.0 }; let w = r.width * 0.5 - offset; - for (point, dir) in r.points().equipoints_dir(45.0, true) { + for (point, dir) in r.interfaced_points().equipoints_dir(45.0, true) { draw.mesh("streetlamp.glb", point - dir.perp_up() * w, dir.perp_up()); } } diff --git a/simulation/src/map/map.rs b/simulation/src/map/map.rs index c3de25dd..5bf3d076 100644 --- a/simulation/src/map/map.rs +++ b/simulation/src/map/map.rs @@ -321,6 +321,7 @@ impl Map { self.lots.retain(|_, lot| { let to_remove = lot.parent == road_id; if to_remove { + self.subscribers.dispatch(UpdateType::Road, lot); smap.remove(lot.id); } !to_remove @@ -407,7 +408,7 @@ impl Map { other_end.update_interface_radius(&mut self.roads); #[allow(clippy::indexing_slicing)] // borrowed before - self.roads[x].update_lanes(&mut self.lanes, &mut self.parking); + self.roads[x].update_lanes(&mut self.lanes, &mut self.parking, &self.environment); } #[allow(clippy::indexing_slicing)] // borrowed before diff --git a/simulation/src/map/objects/road.rs b/simulation/src/map/objects/road.rs index 6a565db9..50c24fb2 100644 --- a/simulation/src/map/objects/road.rs +++ b/simulation/src/map/objects/road.rs @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize}; use slotmapd::new_key_type; use geom::PolyLine; -use geom::{lerp, BoldLine, Degrees, PolyLine3, Spline, Spline1}; +use geom::{BoldLine, Degrees, PolyLine3, Spline, Spline1}; use geom::{Vec2, Vec3}; use crate::map::{ @@ -81,17 +81,13 @@ impl Road { spatial: &mut SpatialMap, ) -> RoadID { let width = lane_pattern.width(); - let points = match Self::generate_points( + let (points, _err) = Self::generate_points( src.pos, dst.pos, segment, lane_pattern.lanes().any(|(a, _, _)| a.is_rail()), env, - ) { - Ok(v) => v, - Err((Some(v), _)) => v, - _ => PolyLine3::new(vec![src.pos, dst.pos]), - }; + ); let id = roads.insert_with_key(|id| Self { id, @@ -121,7 +117,7 @@ impl Road { dist_from_bottom += lane_k.width(); } - road.update_lanes(lanes, parking); + road.update_lanes(lanes, parking, env); spatial.insert(id, road.boldline()); road.id @@ -197,8 +193,13 @@ impl Road { } } - pub fn update_lanes(&mut self, lanes: &mut Lanes, parking: &mut ParkingSpots) { - self.update_interfaced_points(); + pub fn update_lanes( + &mut self, + lanes: &mut Lanes, + parking: &mut ParkingSpots, + env: &Environment, + ) { + self.update_interfaced_points(env); for (id, _) in self.lanes_iter() { let l = unwrap_contlog!(lanes.get_mut(id), "lane in road does not exist anymore"); l.gen_pos(self); @@ -276,27 +277,24 @@ impl Road { &self.interfaced_points } - fn update_interfaced_points(&mut self) { + fn update_interfaced_points(&mut self, env: &Environment) { let points = &self.points; self.interfaced_points = points.cut(self.interface_from(self.src), self.interface_from(self.dst)); let cpoints = &mut self.interfaced_points; - let z_beg = self.points.first().z; - let z_end = self.points.last().z; - - let start = cpoints.first().clone(); - let end = cpoints.last().clone(); - - for v in cpoints.iter_mut_unchecked() { - let start_coeff = v.distance(start) / 15.0; - v.z = lerp(z_beg, v.z, start_coeff.clamp(0.0, 1.0)); - - let end_coeff = v.distance(end) / 15.0; - v.z = lerp(z_end, v.z, end_coeff.clamp(0.0, 1.0)); - } + let z_beg = self.points.first().z - ROAD_Z_OFFSET; + let z_end = self.points.last().z - ROAD_Z_OFFSET; + + let (p, _) = Self::heightfinder( + &PolyLine::new(cpoints.iter().map(|v| v.xy()).collect::>()), + z_beg, + z_end, + MAX_SLOPE, + env, + ); - cpoints.recalculate_length(); + self.interfaced_points = p; } // Run an algorithm to find the height of the road at each point @@ -316,12 +314,13 @@ impl Road { end_height: f32, maxslope: f32, env: &Environment, - ) -> Result, PointGenerateError)> { + ) -> (PolyLine3, Option) { // first calculate the contour let mut contour = Vec::with_capacity(p.length() as usize + 2); let mut points = Vec::with_capacity(contour.len()); + let mut height_error = false; for pos in std::iter::once(p.first()) .chain( p.points_dirs_along((1..p.length() as u32).map(|v| v as f32)) @@ -329,9 +328,10 @@ impl Road { ) .chain(std::iter::once(p.last())) { - let h = env - .height(pos) - .ok_or((None, PointGenerateError::OutsideOfMap))?; + let h = env.height(pos).unwrap_or_else(|| { + height_error = true; + 0.0 + }); contour.push(h); points.push(pos.z(h)); } @@ -445,11 +445,15 @@ impl Road { let mut points = PolyLine3::new(points); points.simplify(Degrees(1.0).into(), 1.0, 100.0); + if height_error { + return (points, Some(PointGenerateError::OutsideOfMap)); + } + if slope_was_too_steep { - return Err((Some(points), PointGenerateError::TooSteep)); + return (points, Some(PointGenerateError::TooSteep)); } - Ok(points) + (points, None) } pub fn generate_points( @@ -458,7 +462,7 @@ impl Road { segment: RoadSegmentKind, precise: bool, env: &Environment, - ) -> Result, PointGenerateError)> { + ) -> (PolyLine3, Option) { let spline = match segment { RoadSegmentKind::Straight => { let p = PolyLine::new(vec![from.xy(), to.xy()]);