use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::io::{Cursor, Read, Write};
use super::MAX_VERTS_PER_POLY;
use super::nav_mesh::{BVNode, MeshTile, OffMeshConnection, Poly, PolyDetail, TileHeader};
use super::{NavMesh, NavMeshParams, PolyFlags, PolyRef, PolyType};
use crate::error::DetourError;
pub const DT_NAVMESH_MAGIC: u32 = 0x5641_4E44;
pub const DT_NAVMESH_VERSION: u32 = 7;
const DT_NAVMESH_STATE_MAGIC: u32 = 0x534D_4E44;
const DT_NAVMESH_STATE_VERSION: u32 = 1;
const DT_VERTS_PER_POLYGON: usize = 6;
const DT_NULL_LINK: u32 = 0xffffffff;
#[allow(dead_code)]
const DT_EXT_LINK: u16 = 0x8000;
fn align4(value: usize) -> usize {
(value + 3) & !3
}
#[repr(C)]
#[derive(Debug, Clone)]
struct TileState {
magic: u32,
version: u32,
tile_ref: PolyRef,
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
struct PolyState {
flags: u16,
area: u8,
}
impl TileState {
fn write_to<W: Write>(&self, writer: &mut W) -> Result<(), DetourError> {
writer.write_u32::<LittleEndian>(self.magic)?;
writer.write_u32::<LittleEndian>(self.version)?;
writer.write_u32::<LittleEndian>(self.tile_ref.id())?;
Ok(())
}
fn read_from<R: Read>(reader: &mut R) -> Result<Self, DetourError> {
Ok(Self {
magic: reader.read_u32::<LittleEndian>()?,
version: reader.read_u32::<LittleEndian>()?,
tile_ref: PolyRef::new(reader.read_u32::<LittleEndian>()?),
})
}
}
impl PolyState {
fn write_to<W: Write>(&self, writer: &mut W) -> Result<(), DetourError> {
writer.write_u16::<LittleEndian>(self.flags)?;
writer.write_u8(self.area)?;
Ok(())
}
fn read_from<R: Read>(reader: &mut R) -> Result<Self, DetourError> {
Ok(Self {
flags: reader.read_u16::<LittleEndian>()?,
area: reader.read_u8()?,
})
}
}
#[repr(C)]
#[derive(Debug, Clone)]
struct MeshHeader {
magic: u32,
version: u32,
x: i32,
y: i32,
layer: i32,
user_id: u32,
poly_count: i32,
vert_count: i32,
max_link_count: i32,
detail_mesh_count: i32,
detail_vert_count: i32,
detail_tri_count: i32,
bv_node_count: i32,
off_mesh_con_count: i32,
off_mesh_base: i32,
walkable_height: f32,
walkable_radius: f32,
walkable_climb: f32,
bmin: [f32; 3],
bmax: [f32; 3],
bv_quant_factor: f32,
}
impl MeshHeader {
fn read_from<R: Read>(reader: &mut R) -> Result<Self, DetourError> {
Ok(Self {
magic: reader.read_u32::<LittleEndian>()?,
version: reader.read_u32::<LittleEndian>()?,
x: reader.read_i32::<LittleEndian>()?,
y: reader.read_i32::<LittleEndian>()?,
layer: reader.read_i32::<LittleEndian>()?,
user_id: reader.read_u32::<LittleEndian>()?,
poly_count: reader.read_i32::<LittleEndian>()?,
vert_count: reader.read_i32::<LittleEndian>()?,
max_link_count: reader.read_i32::<LittleEndian>()?,
detail_mesh_count: reader.read_i32::<LittleEndian>()?,
detail_vert_count: reader.read_i32::<LittleEndian>()?,
detail_tri_count: reader.read_i32::<LittleEndian>()?,
bv_node_count: reader.read_i32::<LittleEndian>()?,
off_mesh_con_count: reader.read_i32::<LittleEndian>()?,
off_mesh_base: reader.read_i32::<LittleEndian>()?,
walkable_height: reader.read_f32::<LittleEndian>()?,
walkable_radius: reader.read_f32::<LittleEndian>()?,
walkable_climb: reader.read_f32::<LittleEndian>()?,
bmin: [
reader.read_f32::<LittleEndian>()?,
reader.read_f32::<LittleEndian>()?,
reader.read_f32::<LittleEndian>()?,
],
bmax: [
reader.read_f32::<LittleEndian>()?,
reader.read_f32::<LittleEndian>()?,
reader.read_f32::<LittleEndian>()?,
],
bv_quant_factor: reader.read_f32::<LittleEndian>()?,
})
}
fn write_to<W: Write>(&self, writer: &mut W) -> Result<(), DetourError> {
writer.write_u32::<LittleEndian>(self.magic)?;
writer.write_u32::<LittleEndian>(self.version)?;
writer.write_i32::<LittleEndian>(self.x)?;
writer.write_i32::<LittleEndian>(self.y)?;
writer.write_i32::<LittleEndian>(self.layer)?;
writer.write_u32::<LittleEndian>(self.user_id)?;
writer.write_i32::<LittleEndian>(self.poly_count)?;
writer.write_i32::<LittleEndian>(self.vert_count)?;
writer.write_i32::<LittleEndian>(self.max_link_count)?;
writer.write_i32::<LittleEndian>(self.detail_mesh_count)?;
writer.write_i32::<LittleEndian>(self.detail_vert_count)?;
writer.write_i32::<LittleEndian>(self.detail_tri_count)?;
writer.write_i32::<LittleEndian>(self.bv_node_count)?;
writer.write_i32::<LittleEndian>(self.off_mesh_con_count)?;
writer.write_i32::<LittleEndian>(self.off_mesh_base)?;
writer.write_f32::<LittleEndian>(self.walkable_height)?;
writer.write_f32::<LittleEndian>(self.walkable_radius)?;
writer.write_f32::<LittleEndian>(self.walkable_climb)?;
writer.write_f32::<LittleEndian>(self.bmin[0])?;
writer.write_f32::<LittleEndian>(self.bmin[1])?;
writer.write_f32::<LittleEndian>(self.bmin[2])?;
writer.write_f32::<LittleEndian>(self.bmax[0])?;
writer.write_f32::<LittleEndian>(self.bmax[1])?;
writer.write_f32::<LittleEndian>(self.bmax[2])?;
writer.write_f32::<LittleEndian>(self.bv_quant_factor)?;
Ok(())
}
}
#[repr(C)]
#[derive(Debug, Clone)]
struct PolyData {
first_link: u32,
verts: [u16; DT_VERTS_PER_POLYGON],
neis: [u16; DT_VERTS_PER_POLYGON],
flags: u16,
vert_count: u8,
area_and_type: u8,
}
impl PolyData {
fn read_from<R: Read>(reader: &mut R) -> Result<Self, DetourError> {
let first_link = reader.read_u32::<LittleEndian>()?;
let mut verts = [0u16; DT_VERTS_PER_POLYGON];
for v in &mut verts {
*v = reader.read_u16::<LittleEndian>()?;
}
let mut neis = [0u16; DT_VERTS_PER_POLYGON];
for n in &mut neis {
*n = reader.read_u16::<LittleEndian>()?;
}
let flags = reader.read_u16::<LittleEndian>()?;
let vert_count = reader.read_u8()?;
let area_and_type = reader.read_u8()?;
Ok(Self {
first_link,
verts,
neis,
flags,
vert_count,
area_and_type,
})
}
fn write_to<W: Write>(&self, writer: &mut W) -> Result<(), DetourError> {
writer.write_u32::<LittleEndian>(self.first_link)?;
for &v in &self.verts {
writer.write_u16::<LittleEndian>(v)?;
}
for &n in &self.neis {
writer.write_u16::<LittleEndian>(n)?;
}
writer.write_u16::<LittleEndian>(self.flags)?;
writer.write_u8(self.vert_count)?;
writer.write_u8(self.area_and_type)?;
Ok(())
}
fn to_poly(&self) -> Poly {
let area = self.area_and_type & 0x3F;
let poly_type = if (self.area_and_type >> 6) == 1 {
PolyType::OffMeshConnection
} else {
PolyType::Ground
};
let flags = PolyFlags::from_bits_truncate(self.flags);
let mut poly = Poly::new(area, poly_type, flags);
poly.first_link = if self.first_link == DT_NULL_LINK {
None
} else {
Some(self.first_link as usize)
};
for i in 0..self.vert_count as usize {
poly.verts[i] = self.verts[i];
}
poly.vert_count = self.vert_count;
for i in 0..DT_VERTS_PER_POLYGON {
poly.neighbors[i] = self.neis[i];
}
poly
}
fn from_poly(poly: &Poly) -> Self {
let mut data = Self {
first_link: poly.first_link.map(|l| l as u32).unwrap_or(DT_NULL_LINK),
verts: [0; DT_VERTS_PER_POLYGON],
neis: [0; DT_VERTS_PER_POLYGON],
flags: poly.flags.bits(),
vert_count: poly.vert_count,
area_and_type: poly.area | ((poly.poly_type as u8) << 6),
};
for i in 0..DT_VERTS_PER_POLYGON.min(MAX_VERTS_PER_POLY) {
data.verts[i] = poly.verts[i];
data.neis[i] = poly.neighbors[i];
}
data
}
}
#[repr(C)]
#[derive(Debug, Clone)]
struct BVNodeData {
bmin: [u16; 3],
bmax: [u16; 3],
i: i32,
}
impl BVNodeData {
fn read_from<R: Read>(reader: &mut R) -> Result<Self, DetourError> {
Ok(Self {
bmin: [
reader.read_u16::<LittleEndian>()?,
reader.read_u16::<LittleEndian>()?,
reader.read_u16::<LittleEndian>()?,
],
bmax: [
reader.read_u16::<LittleEndian>()?,
reader.read_u16::<LittleEndian>()?,
reader.read_u16::<LittleEndian>()?,
],
i: reader.read_i32::<LittleEndian>()?,
})
}
fn write_to<W: Write>(&self, writer: &mut W) -> Result<(), DetourError> {
writer.write_u16::<LittleEndian>(self.bmin[0])?;
writer.write_u16::<LittleEndian>(self.bmin[1])?;
writer.write_u16::<LittleEndian>(self.bmin[2])?;
writer.write_u16::<LittleEndian>(self.bmax[0])?;
writer.write_u16::<LittleEndian>(self.bmax[1])?;
writer.write_u16::<LittleEndian>(self.bmax[2])?;
writer.write_i32::<LittleEndian>(self.i)?;
Ok(())
}
}
#[repr(C)]
#[derive(Debug, Clone)]
struct OffMeshConnectionData {
pos: [f32; 6],
rad: f32,
poly: u16,
flags: u8,
side: u8,
area: u8,
user_id: u32,
}
impl OffMeshConnectionData {
fn read_from<R: Read>(reader: &mut R) -> Result<Self, DetourError> {
let mut pos = [0.0; 6];
for p in &mut pos {
*p = reader.read_f32::<LittleEndian>()?;
}
Ok(Self {
pos,
rad: reader.read_f32::<LittleEndian>()?,
poly: reader.read_u16::<LittleEndian>()?,
flags: reader.read_u8()?,
side: reader.read_u8()?,
area: reader.read_u8()?,
user_id: reader.read_u32::<LittleEndian>()?,
})
}
fn write_to<W: Write>(&self, writer: &mut W) -> Result<(), DetourError> {
for &p in &self.pos {
writer.write_f32::<LittleEndian>(p)?;
}
writer.write_f32::<LittleEndian>(self.rad)?;
writer.write_u16::<LittleEndian>(self.poly)?;
writer.write_u8(self.flags)?;
writer.write_u8(self.side)?;
writer.write_u8(self.area)?;
writer.write_u32::<LittleEndian>(self.user_id)?;
Ok(())
}
}
pub fn load_tile_from_binary(data: &[u8]) -> Result<MeshTile, DetourError> {
let mut cursor = Cursor::new(data);
let header = MeshHeader::read_from(&mut cursor)?;
if header.magic != DT_NAVMESH_MAGIC {
return Err(DetourError::WrongMagic);
}
if header.version != DT_NAVMESH_VERSION {
return Err(DetourError::WrongVersion);
}
let mut tile = MeshTile::new();
tile.header = Some(TileHeader {
x: header.x,
y: header.y,
layer: header.layer,
user_id: header.user_id,
data_size: data.len(),
bmin: header.bmin,
bmax: header.bmax,
poly_count: header.poly_count,
vert_count: header.vert_count,
max_links: header.max_link_count,
detail_mesh_count: header.detail_mesh_count,
detail_vert_count: header.detail_vert_count,
detail_tri_count: header.detail_tri_count,
bvh_node_count: header.bv_node_count,
off_mesh_connection_count: header.off_mesh_con_count,
bv_quant_factor: header.bv_quant_factor,
});
tile.verts.reserve((header.vert_count * 3) as usize);
for _ in 0..header.vert_count * 3 {
tile.verts.push(cursor.read_f32::<LittleEndian>()?);
}
tile.polys.reserve(header.poly_count as usize);
for _ in 0..header.poly_count {
let poly_data = PolyData::read_from(&mut cursor)?;
tile.polys.push(poly_data.to_poly());
}
let link_size = 4 + 4 + 1 + 1 + 1 + 1; cursor.set_position(cursor.position() + (header.max_link_count as u64 * link_size as u64));
if header.detail_mesh_count > 0 {
tile.detail_meshes
.reserve(header.detail_mesh_count as usize);
for _ in 0..header.detail_mesh_count {
let detail = PolyDetail {
vert_base: cursor.read_u32::<LittleEndian>()?,
tri_base: cursor.read_u32::<LittleEndian>()?,
vert_count: cursor.read_u8()?,
tri_count: cursor.read_u8()?,
};
cursor.read_u8()?;
cursor.read_u8()?;
tile.detail_meshes.push(detail);
}
tile.detail_verts
.reserve((header.detail_vert_count * 3) as usize);
for _ in 0..header.detail_vert_count * 3 {
tile.detail_verts.push(cursor.read_f32::<LittleEndian>()?);
}
tile.detail_tris
.reserve((header.detail_tri_count * 4) as usize);
for _ in 0..header.detail_tri_count * 4 {
tile.detail_tris.push(cursor.read_u8()?);
}
}
if header.bv_node_count > 0 {
tile.bvh_nodes.reserve(header.bv_node_count as usize);
for _ in 0..header.bv_node_count {
let node_data = BVNodeData::read_from(&mut cursor)?;
tile.bvh_nodes.push(BVNode {
bmin: node_data.bmin,
bmax: node_data.bmax,
i: node_data.i,
});
}
}
if header.off_mesh_con_count > 0 {
tile.off_mesh_connections
.reserve(header.off_mesh_con_count as usize);
for _ in 0..header.off_mesh_con_count {
let conn_data = OffMeshConnectionData::read_from(&mut cursor)?;
let mut conn = OffMeshConnection::new();
conn.pos = conn_data.pos;
conn.radius = conn_data.rad;
conn.poly = PolyRef::new(conn_data.poly as u32);
conn.flags = PolyFlags::from_bits_truncate(conn_data.flags as u16);
conn.area = conn_data.area;
conn.dir = conn_data.side;
conn.user_id = conn_data.user_id;
tile.off_mesh_connections.push(conn);
}
}
Ok(tile)
}
pub fn save_tile_to_binary(tile: &MeshTile) -> Result<Vec<u8>, DetourError> {
let mut buffer = Vec::new();
let header_info = tile.header.as_ref().ok_or(DetourError::InvalidParam)?;
let bv_quant_factor = if tile.bvh_nodes.is_empty() {
1.0
} else {
let range = [
header_info.bmax[0] - header_info.bmin[0],
header_info.bmax[1] - header_info.bmin[1],
header_info.bmax[2] - header_info.bmin[2],
];
let max_range = range[0].max(range[1]).max(range[2]);
max_range / 65535.0
};
let header = MeshHeader {
magic: DT_NAVMESH_MAGIC,
version: DT_NAVMESH_VERSION,
x: header_info.x,
y: header_info.y,
layer: header_info.layer,
user_id: header_info.user_id,
poly_count: tile.polys.len() as i32,
vert_count: (tile.verts.len() / 3) as i32,
max_link_count: tile.links.len() as i32,
detail_mesh_count: tile.detail_meshes.len() as i32,
detail_vert_count: (tile.detail_verts.len() / 3) as i32,
detail_tri_count: (tile.detail_tris.len() / 4) as i32,
bv_node_count: tile.bvh_nodes.len() as i32,
off_mesh_con_count: tile.off_mesh_connections.len() as i32,
off_mesh_base: tile.polys.len() as i32,
walkable_height: 2.0, walkable_radius: 0.6,
walkable_climb: 0.9,
bmin: header_info.bmin,
bmax: header_info.bmax,
bv_quant_factor,
};
header.write_to(&mut buffer)?;
for &v in &tile.verts {
buffer.write_f32::<LittleEndian>(v)?;
}
for poly in &tile.polys {
let poly_data = PolyData::from_poly(poly);
poly_data.write_to(&mut buffer)?;
}
for _ in 0..header.max_link_count {
buffer.write_u32::<LittleEndian>(0)?; buffer.write_u32::<LittleEndian>(0)?; buffer.write_u8(0)?; buffer.write_u8(0)?; buffer.write_u8(0)?; buffer.write_u8(0)?; }
for detail in &tile.detail_meshes {
buffer.write_u32::<LittleEndian>(detail.vert_base)?;
buffer.write_u32::<LittleEndian>(detail.tri_base)?;
buffer.write_u8(detail.vert_count)?;
buffer.write_u8(detail.tri_count)?;
buffer.write_u8(0)?;
buffer.write_u8(0)?;
}
for &v in &tile.detail_verts {
buffer.write_f32::<LittleEndian>(v)?;
}
for &t in &tile.detail_tris {
buffer.write_u8(t)?;
}
for node in &tile.bvh_nodes {
let node_data = BVNodeData {
bmin: node.bmin,
bmax: node.bmax,
i: node.i,
};
node_data.write_to(&mut buffer)?;
}
for conn in &tile.off_mesh_connections {
let conn_data = OffMeshConnectionData {
pos: conn.pos,
rad: conn.radius,
poly: conn.poly.id() as u16,
flags: conn.flags.bits() as u8,
side: conn.dir,
area: conn.area,
user_id: conn.user_id,
};
conn_data.write_to(&mut buffer)?;
}
Ok(buffer)
}
#[repr(C)]
#[derive(Debug, Clone)]
struct NavMeshSetHeader {
magic: u32,
version: u32,
tile_count: i32,
params: NavMeshParamsData,
}
#[repr(C)]
#[derive(Debug, Clone)]
struct NavMeshParamsData {
origin: [f32; 3],
tile_width: f32,
tile_height: f32,
max_tiles: i32,
max_polys_per_tile: i32,
}
impl NavMeshSetHeader {
fn write_to<W: Write>(&self, writer: &mut W) -> Result<(), DetourError> {
writer.write_u32::<LittleEndian>(self.magic)?;
writer.write_u32::<LittleEndian>(self.version)?;
writer.write_i32::<LittleEndian>(self.tile_count)?;
for &v in &self.params.origin {
writer.write_f32::<LittleEndian>(v)?;
}
writer.write_f32::<LittleEndian>(self.params.tile_width)?;
writer.write_f32::<LittleEndian>(self.params.tile_height)?;
writer.write_i32::<LittleEndian>(self.params.max_tiles)?;
writer.write_i32::<LittleEndian>(self.params.max_polys_per_tile)?;
Ok(())
}
fn read_from<R: Read>(reader: &mut R) -> Result<Self, DetourError> {
let magic = reader.read_u32::<LittleEndian>()?;
let version = reader.read_u32::<LittleEndian>()?;
let tile_count = reader.read_i32::<LittleEndian>()?;
let mut origin = [0.0; 3];
for v in &mut origin {
*v = reader.read_f32::<LittleEndian>()?;
}
let tile_width = reader.read_f32::<LittleEndian>()?;
let tile_height = reader.read_f32::<LittleEndian>()?;
let max_tiles = reader.read_i32::<LittleEndian>()?;
let max_polys_per_tile = reader.read_i32::<LittleEndian>()?;
Ok(NavMeshSetHeader {
magic,
version,
tile_count,
params: NavMeshParamsData {
origin,
tile_width,
tile_height,
max_tiles,
max_polys_per_tile,
},
})
}
}
pub fn save_nav_mesh_to_binary(nav_mesh: &NavMesh) -> Result<Vec<u8>, DetourError> {
let mut buffer = Vec::new();
let tiles = nav_mesh.get_all_tiles();
let tile_count = tiles.iter().filter(|t| t.header.is_some()).count() as i32;
let params = nav_mesh.get_params();
let header = NavMeshSetHeader {
magic: DT_NAVMESH_MAGIC,
version: DT_NAVMESH_VERSION,
tile_count,
params: NavMeshParamsData {
origin: params.origin,
tile_width: params.tile_width,
tile_height: params.tile_height,
max_tiles: params.max_tiles,
max_polys_per_tile: params.max_polys_per_tile,
},
};
header.write_to(&mut buffer)?;
for tile in tiles {
if tile.header.is_some() {
let tile_data = save_tile_to_binary(tile)?;
buffer.write_u32::<LittleEndian>(tile_data.len() as u32)?;
buffer.extend_from_slice(&tile_data);
}
}
Ok(buffer)
}
pub fn load_nav_mesh_from_binary(data: &[u8]) -> Result<NavMesh, DetourError> {
let mut cursor = Cursor::new(data);
let magic = cursor.read_u32::<LittleEndian>()?;
cursor.set_position(0);
if magic == DT_NAVMESH_MAGIC {
cursor.set_position(4);
let version = cursor.read_u32::<LittleEndian>()?;
cursor.set_position(0);
if version == DT_NAVMESH_VERSION {
if data.len() >= 40 {
let header = NavMeshSetHeader::read_from(&mut cursor)?;
if header.tile_count >= 0 && header.tile_count <= 65536 {
let params = NavMeshParams {
origin: header.params.origin,
tile_width: header.params.tile_width,
tile_height: header.params.tile_height,
max_tiles: header.params.max_tiles,
max_polys_per_tile: header.params.max_polys_per_tile,
};
let mut nav_mesh = NavMesh::new(params)?;
if header.tile_count > 0 {
for _ in 0..header.tile_count {
let tile_size = cursor.read_u32::<LittleEndian>()? as usize;
let pos = cursor.position() as usize;
if pos + tile_size > data.len() {
return Err(DetourError::InvalidParam);
}
let tile_data = &data[pos..pos + tile_size];
let tile = load_tile_from_binary(tile_data)?;
nav_mesh.add_mesh_tile(tile)?;
cursor.set_position((pos + tile_size) as u64);
}
}
return Ok(nav_mesh);
}
}
}
}
let tile = load_tile_from_binary(data)?;
let header = tile.header.as_ref().ok_or(DetourError::InvalidParam)?;
let params = NavMeshParams {
origin: header.bmin,
tile_width: header.bmax[0] - header.bmin[0],
tile_height: header.bmax[2] - header.bmin[2],
max_tiles: 1,
max_polys_per_tile: header.poly_count,
};
let mut nav_mesh = NavMesh::new(params)?;
let _tile_ref = nav_mesh.add_mesh_tile(tile)?;
Ok(nav_mesh)
}
pub fn get_tile_state_size(tile: &MeshTile) -> usize {
let Some(header) = tile.header.as_ref() else {
return 0;
};
let header_size = align4(std::mem::size_of::<TileState>());
let state_size = align4(header.poly_count as usize * std::mem::size_of::<PolyState>());
header_size + state_size
}
pub fn store_tile_state(tile: &MeshTile, tile_ref: PolyRef) -> Result<Vec<u8>, DetourError> {
let header = tile
.header
.as_ref()
.ok_or_else(|| DetourError::InvalidParam)?;
let size = get_tile_state_size(tile);
let mut buffer = Vec::with_capacity(size);
let state_header = TileState {
magic: DT_NAVMESH_STATE_MAGIC,
version: DT_NAVMESH_STATE_VERSION,
tile_ref,
};
state_header.write_to(&mut buffer)?;
while buffer.len() % 4 != 0 {
buffer.push(0);
}
for i in 0..header.poly_count as usize {
if i < tile.polys.len() {
let poly = &tile.polys[i];
let state = PolyState {
flags: poly.flags.bits(),
area: poly.area,
};
state.write_to(&mut buffer)?;
}
}
while buffer.len() % 4 != 0 {
buffer.push(0);
}
Ok(buffer)
}
pub fn restore_tile_state(
tile: &mut MeshTile,
data: &[u8],
expected_ref: PolyRef,
) -> Result<(), DetourError> {
let header = tile
.header
.as_ref()
.ok_or_else(|| DetourError::InvalidParam)?;
let mut cursor = Cursor::new(data);
let state_header = TileState::read_from(&mut cursor)?;
if state_header.magic != DT_NAVMESH_STATE_MAGIC {
return Err(DetourError::WrongMagic);
}
if state_header.version != DT_NAVMESH_STATE_VERSION {
return Err(DetourError::WrongVersion);
}
if state_header.tile_ref != expected_ref {
return Err(DetourError::InvalidParam);
}
let header_size = align4(std::mem::size_of::<TileState>());
cursor.set_position(header_size as u64);
for i in 0..header.poly_count as usize {
if i < tile.polys.len() {
let state = PolyState::read_from(&mut cursor)?;
tile.polys[i].flags = PolyFlags::from_bits_truncate(state.flags);
tile.polys[i].area = state.area;
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_align4() {
assert_eq!(align4(0), 0);
assert_eq!(align4(1), 4);
assert_eq!(align4(2), 4);
assert_eq!(align4(3), 4);
assert_eq!(align4(4), 4);
assert_eq!(align4(5), 8);
}
#[test]
fn test_poly_data_conversion() {
let mut poly = Poly::new(5, PolyType::Ground, PolyFlags::WALK);
poly.verts[0] = 10;
poly.verts[1] = 20;
poly.vert_count = 2;
let data = PolyData::from_poly(&poly);
assert_eq!(data.verts[0], 10);
assert_eq!(data.verts[1], 20);
assert_eq!(data.vert_count, 2);
assert_eq!(data.area_and_type, 5); assert_eq!(data.flags, PolyFlags::WALK.bits());
let poly2 = data.to_poly();
assert_eq!(poly2.verts[0], poly.verts[0]);
assert_eq!(poly2.verts[1], poly.verts[1]);
assert_eq!(poly2.vert_count, poly.vert_count);
assert_eq!(poly2.area, poly.area);
assert_eq!(poly2.poly_type, poly.poly_type);
assert_eq!(poly2.flags, poly.flags);
}
}