oxiphysics_collision/recast/
mod.rs1pub mod compact;
12pub mod contour;
13pub mod polymesh;
14pub mod rasterize;
15pub mod region;
16pub mod walkable;
17
18use crate::recast::polymesh::PolyMesh;
19
20#[derive(Debug, Clone)]
22pub struct RecastConfig {
23 pub cell_size: f64,
25 pub cell_height: f64,
27 pub agent_height: f64,
29 pub agent_radius: f64,
31 pub agent_max_climb: f64,
33 pub agent_max_slope: f64,
35 pub region_min_size: usize,
37 pub region_merge_size: usize,
39 pub edge_max_len: f64,
41 pub edge_max_error: f64,
43 pub max_verts_per_poly: usize,
45}
46
47impl Default for RecastConfig {
48 fn default() -> Self {
49 Self {
50 cell_size: 0.3,
51 cell_height: 0.2,
52 agent_height: 2.0,
53 agent_radius: 0.6,
54 agent_max_climb: 0.9,
55 agent_max_slope: 45.0,
56 region_min_size: 8,
57 region_merge_size: 20,
58 edge_max_len: 12.0,
59 edge_max_error: 1.3,
60 max_verts_per_poly: 6,
61 }
62 }
63}
64
65#[derive(Debug)]
67pub enum RecastError {
68 EmptyInput,
69 RasterizationFailed(String),
70 RegionBuildFailed(String),
71 ContourBuildFailed(String),
72 PolyMeshBuildFailed(String),
73}
74
75impl std::fmt::Display for RecastError {
76 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77 match self {
78 RecastError::EmptyInput => write!(f, "input triangle soup is empty"),
79 RecastError::RasterizationFailed(s) => write!(f, "rasterization failed: {s}"),
80 RecastError::RegionBuildFailed(s) => write!(f, "region build failed: {s}"),
81 RecastError::ContourBuildFailed(s) => write!(f, "contour build failed: {s}"),
82 RecastError::PolyMeshBuildFailed(s) => write!(f, "poly mesh build failed: {s}"),
83 }
84 }
85}
86
87impl std::error::Error for RecastError {}
88
89pub struct RecastBuilder {
91 pub config: RecastConfig,
92}
93
94impl RecastBuilder {
95 pub fn new(config: RecastConfig) -> Self {
96 Self { config }
97 }
98
99 pub fn with_default_config() -> Self {
100 Self::new(RecastConfig::default())
101 }
102
103 pub fn build(&self, tris: &[[f64; 9]]) -> Result<PolyMesh, RecastError> {
108 if tris.is_empty() {
109 return Err(RecastError::EmptyInput);
110 }
111
112 let mut hf = rasterize::rasterize_triangles(tris, &self.config)
114 .map_err(RecastError::RasterizationFailed)?;
115
116 walkable::filter_low_hanging_obstacles(&mut hf, &self.config);
118 walkable::filter_ledge_spans(&mut hf, &self.config);
119 walkable::filter_walkable_low_height(&mut hf, &self.config);
120
121 let chf = compact::build_compact_heightfield(&hf, &self.config);
123
124 let rhf =
126 region::build_regions(&chf, &self.config).map_err(RecastError::RegionBuildFailed)?;
127
128 let cs = contour::build_contours(&rhf, &chf, &self.config)
130 .map_err(RecastError::ContourBuildFailed)?;
131
132 let pm = polymesh::build_poly_mesh(&cs, &self.config)
134 .map_err(RecastError::PolyMeshBuildFailed)?;
135
136 Ok(pm)
137 }
138}