diff --git a/Cargo.lock b/Cargo.lock index 30b19d57..23b18a15 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -480,6 +480,15 @@ dependencies = [ "system-deps", ] +[[package]] +name = "atomic-polyfill" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" +dependencies = [ + "critical-section", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -1903,6 +1912,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "critical-section" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" + [[package]] name = "crossbeam-channel" version = "0.5.8" @@ -2069,6 +2084,16 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" +[[package]] +name = "earcutr" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79127ed59a85d7687c409e9978547cffb7dc79675355ed22da6b66fd5f6ead01" +dependencies = [ + "itertools", + "num-traits", +] + [[package]] name = "ecolor" version = "0.22.0" @@ -2513,6 +2538,44 @@ dependencies = [ "version_check", ] +[[package]] +name = "geo" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4841b40fdbccd4b7042bd6195e4de91da54af34c50632e371bcbfcdfb558b873" +dependencies = [ + "earcutr", + "float_next_after", + "geo-types", + "geographiclib-rs", + "log", + "num-traits", + "robust", + "rstar", + "spade", +] + +[[package]] +name = "geo-types" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567495020b114f1ce9bed679b29975aa0bfae06ac22beacd5cfde5dabe7b05d6" +dependencies = [ + "approx", + "num-traits", + "rstar", + "serde", +] + +[[package]] +name = "geographiclib-rs" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea804e7bd3c6a4ca6a01edfa35231557a8a81d4d3f3e1e2b650d028c42592be" +dependencies = [ + "lazy_static", +] + [[package]] name = "gethostname" version = "0.2.3" @@ -2840,6 +2903,15 @@ dependencies = [ "surf", ] +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -2872,6 +2944,19 @@ dependencies = [ "winapi", ] +[[package]] +name = "heapless" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" +dependencies = [ + "atomic-polyfill", + "hash32", + "rustc_version 0.4.0", + "spin", + "stable_deref_trait", +] + [[package]] name = "heck" version = "0.3.3" @@ -3459,58 +3544,6 @@ dependencies = [ "value-bag", ] -[[package]] -name = "lyon" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e7f9cda98b5430809e63ca5197b06c7d191bf7e26dfc467d5a3f0290e2a74f" -dependencies = [ - "lyon_algorithms", - "lyon_tessellation", -] - -[[package]] -name = "lyon_algorithms" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00a0349cd8f0270781bb93a824b63df6178e3b4a27794e7be3ce3763f5a44d6e" -dependencies = [ - "lyon_path", - "num-traits", -] - -[[package]] -name = "lyon_geom" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74df1ff0a0147282eb10699537a03baa7d31972b58984a1d44ce0624043fe8ad" -dependencies = [ - "arrayvec", - "euclid", - "num-traits", -] - -[[package]] -name = "lyon_path" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca507745ba7ccbc76e5c44e7b63b1a29d2b0d6126f375806a5bbaf657c7d6c45" -dependencies = [ - "lyon_geom", - "num-traits", -] - -[[package]] -name = "lyon_tessellation" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23bcac20d47825850fabf1e869bf7c2bbe2daefa0776c3cd2eb7cb74635f6e4a" -dependencies = [ - "float_next_after", - "lyon_path", - "thiserror", -] - [[package]] name = "mach2" version = "0.4.1" @@ -4596,9 +4629,9 @@ dependencies = [ "crossbeam-channel", "dirs", "futures-lite 1.13.0", + "geo", "gz-fuel", "itertools", - "lyon", "pathdiff", "rfd", "rmf_site_format", @@ -4635,6 +4668,12 @@ dependencies = [ "uuid", ] +[[package]] +name = "robust" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf4a6aa5f6d6888f39e980649f3ad6b666acdce1d78e95b8a2cb076e687ae30" + [[package]] name = "rodio" version = "0.17.3" @@ -4657,6 +4696,17 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "rstar" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73111312eb7a2287d229f06c00ff35b51ddee180f017ab6dec1f69d62ac098d6" +dependencies = [ + "heapless", + "num-traits", + "smallvec", +] + [[package]] name = "rustc-demangle" version = "0.1.23" @@ -4675,7 +4725,16 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" dependencies = [ - "semver", + "semver 0.9.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver 1.0.21", ] [[package]] @@ -4781,6 +4840,12 @@ dependencies = [ "semver-parser", ] +[[package]] +name = "semver" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" + [[package]] name = "semver-parser" version = "0.7.0" @@ -5019,6 +5084,27 @@ dependencies = [ "winapi", ] +[[package]] +name = "spade" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61addf9117b11d1f5b4bf6fe94242ba25f59d2d4b2080544b771bd647024fd00" +dependencies = [ + "hashbrown 0.14.2", + "num-traits", + "robust", + "smallvec", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + [[package]] name = "spinning_top" version = "0.2.5" @@ -5038,6 +5124,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "standback" version = "0.2.17" @@ -5060,7 +5152,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" dependencies = [ "discard", - "rustc_version", + "rustc_version 0.2.3", "stdweb-derive", "stdweb-internal-macros", "stdweb-internal-runtime", diff --git a/rmf_site_editor/Cargo.toml b/rmf_site_editor/Cargo.toml index 96173862..cc64be29 100644 --- a/rmf_site_editor/Cargo.toml +++ b/rmf_site_editor/Cargo.toml @@ -34,7 +34,7 @@ bevy = { version = "0.11", features = ["pnm", "jpeg", "tga"] } bevy_utils = "0.11" dirs = "5.0" thread_local = "*" -lyon = "1" +geo = "0.27" thiserror = "*" rmf_site_format = { path = "../rmf_site_format", features = ["bevy"] } itertools = "*" diff --git a/rmf_site_editor/src/site/floor.rs b/rmf_site_editor/src/site/floor.rs index 8db208d4..053f816e 100644 --- a/rmf_site_editor/src/site/floor.rs +++ b/rmf_site_editor/src/site/floor.rs @@ -21,10 +21,9 @@ use bevy::{ prelude::*, render::mesh::{Indices, PrimitiveTopology}, }; -use lyon::{ - math::point, - path::Path as LyonPath, - tessellation::{geometry_builder::simple_builder, *}, +use geo::{ + geometry::{LineString, MultiPolygon, Polygon}, + BooleanOps, CoordsIter, TriangulateSpade, }; use rmf_site_format::{FloorMarker, Path, Texture}; @@ -70,6 +69,7 @@ fn make_floor_mesh( anchor_path: &Path, texture: &Texture, anchors: &AnchorParams, + lifts: &Query<(&Transform, &LiftCabin)>, ) -> Mesh { if anchor_path.len() == 0 { return Mesh::new(PrimitiveTopology::TriangleList); @@ -99,71 +99,83 @@ fn make_floor_mesh( .into(); } - let mut builder = LyonPath::builder(); - let mut first = true; - let mut valid = true; let mut reference_positions = Vec::new(); + let mut valid = true; for anchor in &anchor_path.0 { - let p = match anchors.point_in_parent_frame_of(*anchor, Category::Floor, entity) { - Ok(a) => a, + match anchors.point_in_parent_frame_of(*anchor, Category::Floor, entity) { + Ok(p) => reference_positions.push(p.to_array()), Err(_) => { error!("Failed to find anchor {anchor:?} used by a path"); valid = false; continue; } }; - - reference_positions.push(p.to_array()); - if first { - first = false; - builder.begin(point(p.x, p.y)); - } else { - builder.line_to(point(p.x, p.y)); - } } + let mut polygon = MultiPolygon::from(Polygon::new( + LineString::from( + reference_positions + .iter() + .map(|p| [p[0], p[1]]) + .collect::>(), + ), + vec![], + )); let outline_buffer = make_closed_path_outline(reference_positions); if !valid { return make_fallback_floor_mesh_near_path(entity, anchor_path, anchors); } - - builder.close(); - let path = builder.build(); - - let mut buffers = VertexBuffers::new(); - { - let mut vertex_builder = simple_builder(&mut buffers); - let mut tessellator = FillTessellator::new(); - let result = tessellator.tessellate_path( - path.as_slice(), - &FillOptions::default(), - &mut vertex_builder, - ); - - match result { - Err(err) => { - error!("Failed to render floor: {err}"); - return make_fallback_floor_mesh_near_path(entity, anchor_path, anchors); - } - _ => {} + // Subtract all the lift cabin AABBs + for (tf, cabin) in lifts.iter() { + let to_subtract = match cabin { + LiftCabin::Rect(params) => { + let w = params.thickness(); + let gap_for_door = |d: &Option>| -> f32 { + d.and_then(|d| d.custom_gap).unwrap_or(params.gap()) + w + }; + let aabb = params.aabb(); + let tf_cabin = *tf * Transform::from_translation(aabb.center.into()); + let front_gap = gap_for_door(¶ms.front_door); + let right_gap = gap_for_door(¶ms.right_door); + let back_gap = gap_for_door(¶ms.back_door); + let left_gap = gap_for_door(¶ms.left_door); + let he = aabb.half_extents; + let he0 = Vec3::new(he.x + front_gap, he.y + left_gap, 0.0); + let he1 = Vec3::new(he.x + front_gap, -he.y - right_gap, 0.0); + let he2 = Vec3::new(-he.x - back_gap, -he.y - right_gap, 0.0); + let he3 = Vec3::new(-he.x - back_gap, he.y + left_gap, 0.0); + let p0 = tf_cabin.transform_point(he0); + let p1 = tf_cabin.transform_point(he1); + let p2 = tf_cabin.transform_point(he2); + let p3 = tf_cabin.transform_point(he3); + Polygon::new( + LineString::from(vec![[p0.x, p0.y], [p1.x, p1.y], [p2.x, p2.y], [p3.x, p3.y]]), + vec![], + ) + } // When new lift types are added, add their footprint calculation here. + }; + polygon = polygon.difference(&to_subtract.into()); + } + let mut positions: Vec<[f32; 3]> = Vec::new(); + for polygon in polygon.iter() { + let Ok(triangles) = polygon.constrained_triangulation(Default::default()) else { + warn!("Failed triangulating lift floor hole"); + continue; + }; + positions.reserve(triangles.len() * 3); + for triangle in triangles.iter() { + positions.extend(triangle.coords_iter().map(|v| [v.x, v.y, 0.])); } } let texture_width = texture.width.unwrap_or(1.0); let texture_height = texture.height.unwrap_or(1.0); - let positions: Vec<[f32; 3]> = buffers.vertices.iter().map(|v| [v.x, v.y, 0.]).collect(); - let normals: Vec<[f32; 3]> = buffers.vertices.iter().map(|_| [0., 0., 1.]).collect(); - let uv: Vec<[f32; 2]> = buffers - .vertices + let indices = (0..positions.len() as u32).collect(); + let normals: Vec<[f32; 3]> = positions.iter().map(|_| [0., 0., 1.]).collect(); + let uv: Vec<[f32; 2]> = positions .iter() - .map(|v| [v.x / texture_width, v.y / texture_height]) + .map(|v| [v[0] / texture_width, v[1] / texture_height]) .collect(); - for i in 0..buffers.indices.len() / 3 { - let i1 = 3 * i + 1; - let i2 = 3 * i + 2; - buffers.indices.swap(i1, i2); - } - let indices = buffers.indices.drain(..).map(|v| v as u32).collect(); MeshBuffer::new(positions, normals, indices) .with_uv(uv) @@ -224,11 +236,12 @@ pub fn add_floor_visuals( mut meshes: ResMut>, mut materials: ResMut>, default_floor_vis: Query<(&GlobalFloorVisibility, &RecencyRanking)>, + lifts: Query<(&Transform, &LiftCabin)>, ) { for (e, new_floor, texture_source, rank, vis, parent) in &floors { let (base_color_texture, texture) = from_texture_source(texture_source, &textures); - let mesh = make_floor_mesh(e, new_floor, &texture, &anchors); + let mesh = make_floor_mesh(e, new_floor, &texture, &anchors, &lifts); let height = floor_height(rank); let default_vis = parent .map(|p| default_floor_vis.get(p.get()).ok()) @@ -296,13 +309,14 @@ pub fn update_floors_for_moved_anchors( >, mut mesh_assets: ResMut>, mut mesh_handles: Query<&mut Handle>, + lifts: Query<(&Transform, &LiftCabin)>, ) { for dependents in &changed_anchors { for dependent in dependents.iter() { if let Some((e, segments, path, texture_source)) = floors.get(*dependent).ok() { let (_, texture) = from_texture_source(texture_source, &textures); if let Ok(mut mesh) = mesh_handles.get_mut(segments.mesh) { - *mesh = mesh_assets.add(make_floor_mesh(e, path, &texture, &anchors)); + *mesh = mesh_assets.add(make_floor_mesh(e, path, &texture, &anchors, &lifts)); } } } @@ -328,6 +342,7 @@ pub fn update_floors( material_handles: Query<&Handle>, anchors: AnchorParams, textures: Query<(Option<&Handle>, &Texture)>, + lifts: Query<(&Transform, &LiftCabin)>, ) { for e in changed_floors.iter().chain( changed_texture_sources @@ -340,7 +355,7 @@ pub fn update_floors( let (base_color_texture, texture) = from_texture_source(texture_source, &textures); if let Ok(mut mesh) = mesh_handles.get_mut(segment.mesh) { if let Ok(material) = material_handles.get(segment.mesh) { - *mesh = meshes.add(make_floor_mesh(e, path, &texture, &anchors)); + *mesh = meshes.add(make_floor_mesh(e, path, &texture, &anchors, &lifts)); if let Some(mut material) = materials.get_mut(material) { material.base_color_texture = base_color_texture; } @@ -349,6 +364,33 @@ pub fn update_floors( } } +pub fn update_floors_for_changed_lifts( + lifts: Query<(&Transform, &LiftCabin)>, + changed_lifts: Query< + (), + Or<( + Changed>, + (With>, Changed), + )>, + >, + removed_lifts: RemovedComponents>, + floors: Query<(Entity, &FloorSegments, &Path, &Affiliation), With>, + anchors: AnchorParams, + textures: Query<(Option<&Handle>, &Texture)>, + mut mesh_assets: ResMut>, + mut mesh_handles: Query<&mut Handle>, +) { + if changed_lifts.is_empty() && removed_lifts.is_empty() { + return; + } + for (e, segments, path, texture_source) in floors.iter() { + let (_, texture) = from_texture_source(texture_source, &textures); + if let Ok(mut mesh) = mesh_handles.get_mut(segments.mesh) { + *mesh = mesh_assets.add(make_floor_mesh(e, path, &texture, &anchors, &lifts)); + } + } +} + #[inline] fn iter_update_floor_visibility<'a>( iter: impl Iterator< diff --git a/rmf_site_editor/src/site/mod.rs b/rmf_site_editor/src/site/mod.rs index fbd3c96c..03852f55 100644 --- a/rmf_site_editor/src/site/mod.rs +++ b/rmf_site_editor/src/site/mod.rs @@ -338,7 +338,7 @@ impl Plugin for SitePlugin { add_floor_visuals, update_floors, update_floors_for_moved_anchors, - update_floors, + update_floors_for_changed_lifts, update_floor_visibility, update_drawing_visibility, add_lane_visuals,