recastnavigation_rs/detour/
builder.rs

1use cxx::{type_id, ExternType};
2use static_assertions::const_assert_eq;
3use std::fmt::{self, Debug, Formatter};
4use std::mem;
5
6use crate::base::XError;
7use crate::detour::base::{DtAABB, DtBuf};
8use crate::detour::mesh::DT_VERTS_PER_POLYGON;
9
10#[cxx::bridge]
11pub(crate) mod ffi {
12    unsafe extern "C++" {
13        include!("recastnavigation-rs/src/detour/detour-ffi.h");
14
15        type dtNavMeshCreateParams = crate::detour::builder::CxxDtNavMeshCreateParams;
16
17        unsafe fn dtCreateNavMeshData(
18            params: *mut dtNavMeshCreateParams,
19            outData: *mut *mut u8,
20            outDataSize: *mut i32,
21        ) -> bool;
22        unsafe fn dtNavMeshHeaderSwapEndian(data: *mut u8, dataSize: i32) -> bool;
23        unsafe fn dtNavMeshDataSwapEndian(data: *mut u8, dataSize: i32) -> bool;
24    }
25}
26
27// Represents the source data used to build a navigation mesh tile.
28#[derive(Default)]
29pub struct DtNavMeshCreateParams<'t> {
30    pub verts: Option<&'t [[u16; 3]]>,
31    pub polys: Option<&'t [u16]>,
32    pub poly_flags: Option<&'t [u16]>,
33    pub poly_areas: Option<&'t [u8]>,
34    pub nvp: usize,
35
36    pub detail_meshes: Option<&'t [[u32; 4]]>,
37    pub detail_verts: Option<&'t [[f32; 3]]>,
38    pub detail_tris: Option<&'t [[u8; 4]]>,
39
40    pub off_mesh_con_verts: Option<&'t [DtAABB]>,
41    pub off_mesh_con_rad: Option<&'t [f32]>,
42    pub off_mesh_con_flags: Option<&'t [u16]>,
43    pub off_mesh_con_areas: Option<&'t [u8]>,
44    pub off_mesh_con_dir: Option<&'t [u8]>,
45    pub off_mesh_con_user_id: Option<&'t [u32]>,
46
47    pub user_id: u32,
48    pub tile_x: i32,
49    pub tile_y: i32,
50    pub tile_layer: i32,
51    pub bmin: [f32; 3],
52    pub bmax: [f32; 3],
53
54    pub walkable_height: f32,
55    pub walkable_radius: f32,
56    pub walkable_climb: f32,
57    pub cs: f32,
58    pub ch: f32,
59
60    pub build_bv_tree: bool,
61}
62
63impl Debug for DtNavMeshCreateParams<'_> {
64    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
65        let cp = CxxDtNavMeshCreateParams::from(self);
66        return write!(f, "{:?}", cp);
67    }
68}
69
70fn unpack_ptr<T>(v: Option<&[T]>) -> *const T {
71    return v.map(|v| v.as_ptr()).unwrap_or(std::ptr::null());
72}
73
74fn unpack_len<T>(v: Option<&[T]>) -> i32 {
75    return v.map(|v| v.len() as i32).unwrap_or(0);
76}
77
78#[repr(C)]
79#[derive(Debug)]
80pub(crate) struct CxxDtNavMeshCreateParams {
81    verts: *const u16,
82    vert_count: i32,
83    polys: *const u16,
84    poly_flags: *const u16,
85    poly_areas: *const u8,
86    poly_count: i32,
87    nvp: i32,
88
89    detail_meshes: *const u32,
90    detail_verts: *const f32,
91    detail_verts_count: i32,
92    detail_tris: *const u8,
93    detail_tri_count: i32,
94
95    off_mesh_con_verts: *const f32,
96    off_mesh_con_rad: *const f32,
97    off_mesh_con_flags: *const u16,
98    off_mesh_con_areas: *const u8,
99    off_mesh_con_dir: *const u8,
100    off_mesh_con_user_id: *const u32,
101    off_mesh_con_count: i32,
102
103    user_id: u32,
104    tile_x: i32,
105    tile_y: i32,
106    tile_layer: i32,
107    bmin: [f32; 3],
108    bmax: [f32; 3],
109
110    walkable_height: f32,
111    walkable_radius: f32,
112    walkable_climb: f32,
113    cs: f32,
114    ch: f32,
115
116    build_bv_tree: bool,
117}
118
119#[cfg(target_pointer_width = "32")]
120const_assert_eq!(mem::size_of::<CxxDtNavMeshCreateParams>(), 140);
121
122#[cfg(target_pointer_width = "64")]
123const_assert_eq!(mem::size_of::<CxxDtNavMeshCreateParams>(), 208);
124
125unsafe impl ExternType for CxxDtNavMeshCreateParams {
126    type Id = type_id!("dtNavMeshCreateParams");
127    type Kind = cxx::kind::Trivial;
128}
129
130impl CxxDtNavMeshCreateParams {
131    fn from(params: &DtNavMeshCreateParams<'_>) -> CxxDtNavMeshCreateParams {
132        return CxxDtNavMeshCreateParams {
133            verts: unpack_ptr(params.verts) as *const _,
134            vert_count: unpack_len(params.verts),
135            polys: unpack_ptr(params.polys),
136            poly_flags: unpack_ptr(params.poly_flags),
137            poly_areas: unpack_ptr(params.poly_areas),
138            poly_count: unpack_len(params.poly_flags),
139            nvp: params.nvp as i32,
140
141            detail_meshes: unpack_ptr(params.detail_meshes) as *const _,
142            detail_verts: unpack_ptr(params.detail_verts) as *const _,
143            detail_verts_count: unpack_len(params.detail_verts),
144            detail_tris: unpack_ptr(params.detail_tris) as *const _,
145            detail_tri_count: unpack_len(params.detail_tris),
146
147            off_mesh_con_verts: unpack_ptr(params.off_mesh_con_verts) as *const _,
148            off_mesh_con_rad: unpack_ptr(params.off_mesh_con_rad),
149            off_mesh_con_flags: unpack_ptr(params.off_mesh_con_flags),
150            off_mesh_con_areas: unpack_ptr(params.off_mesh_con_areas),
151            off_mesh_con_dir: unpack_ptr(params.off_mesh_con_dir),
152            off_mesh_con_user_id: unpack_ptr(params.off_mesh_con_user_id),
153            off_mesh_con_count: unpack_len(params.off_mesh_con_verts),
154
155            user_id: params.user_id,
156            tile_x: params.tile_x,
157            tile_y: params.tile_y,
158            tile_layer: params.tile_layer,
159            bmin: params.bmin,
160            bmax: params.bmax,
161
162            walkable_height: params.walkable_height,
163            walkable_radius: params.walkable_radius,
164            walkable_climb: params.walkable_climb,
165            cs: params.cs,
166            ch: params.ch,
167
168            build_bv_tree: params.build_bv_tree,
169        };
170    }
171}
172
173pub fn dt_create_nav_mesh_data(params: &mut DtNavMeshCreateParams) -> Result<DtBuf, XError> {
174    let mut cp = CxxDtNavMeshCreateParams::from(params);
175
176    if cp.vert_count < 3 || cp.vert_count >= 0xFFFF {
177        return Err(XError::InvalidParam);
178    }
179
180    if cp.poly_count < 1 {
181        return Err(XError::InvalidParam);
182    }
183    if unpack_len(params.polys) != cp.poly_count * 2 * cp.nvp {
184        return Err(XError::InvalidParam);
185    }
186    if unpack_len(params.poly_flags) != cp.poly_count {
187        return Err(XError::InvalidParam);
188    }
189    if unpack_len(params.poly_areas) != cp.poly_count {
190        return Err(XError::InvalidParam);
191    }
192    if cp.nvp < 3 || cp.nvp > DT_VERTS_PER_POLYGON as i32 {
193        return Err(XError::InvalidParam);
194    }
195
196    if cp.detail_meshes.is_null() {
197        if unpack_len(params.detail_meshes) != cp.poly_count {
198            return Err(XError::InvalidParam);
199        }
200    }
201
202    if unpack_len(params.off_mesh_con_rad) != cp.off_mesh_con_count {
203        return Err(XError::InvalidParam);
204    }
205    if unpack_len(params.off_mesh_con_flags) != cp.off_mesh_con_count {
206        return Err(XError::InvalidParam);
207    }
208    if unpack_len(params.off_mesh_con_areas) != cp.off_mesh_con_count {
209        return Err(XError::InvalidParam);
210    }
211    if unpack_len(params.off_mesh_con_dir) != cp.off_mesh_con_count {
212        return Err(XError::InvalidParam);
213    }
214    if unpack_len(params.off_mesh_con_user_id) != cp.off_mesh_con_count {
215        return Err(XError::InvalidParam);
216    }
217
218    unsafe {
219        let mut buf = DtBuf::default();
220        let res = ffi::dtCreateNavMeshData((&mut cp) as *mut _, &mut buf.data, &mut buf.size);
221        if !res {
222            return Err(XError::Failed);
223        }
224        return Ok(buf);
225    }
226}
227
228pub fn dt_nav_mesh_header_swap_endian(buf: &mut DtBuf) -> Result<(), XError> {
229    let res = unsafe { ffi::dtNavMeshHeaderSwapEndian(buf.data, buf.size) };
230    if !res {
231        return Err(XError::Failed);
232    }
233    return Ok(());
234}
235
236pub fn dt_nav_mesh_data_swap_endian(buf: &mut DtBuf) -> Result<(), XError> {
237    let res = unsafe { ffi::dtNavMeshDataSwapEndian(buf.data, buf.size) };
238    if !res {
239        return Err(XError::Failed);
240    }
241    return Ok(());
242}