some documentations

This commit is contained in:
Robin Appelman 2020-05-01 00:24:44 +02:00
commit 4c48efc5cc
5 changed files with 178 additions and 26 deletions

View file

@ -1,4 +1,8 @@
use crate::navmesh::HammerUnit;
pub use crate::navmesh::{
ApproachArea, Connections, EncounterPath, LadderConnections, LadderDirection, LightIntensity,
NavDirection, NavHidingSpot, Vector3, VisibleArea,
};
use crate::parser::read_areas;
pub use crate::parser::{NavArea, ParseError};
use aabb_quadtree::{ItemId, QuadTree};
@ -7,8 +11,22 @@ use euclid::{TypedPoint2D, TypedRect, TypedSize2D};
mod navmesh;
mod parser;
/// A tree of all navigation areas
pub struct NavTree(QuadTree<NavArea, HammerUnit, [(ItemId, TypedRect<f32, HammerUnit>); 4]>);
/// Parse all navigation areas from a nav file
///
/// ## Examples
///
/// ```no_run
/// use sourcenav::get_area_tree;
///
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let file = std::fs::read("path/to/navfile.nav")?;
/// let tree = get_area_tree(file)?;
/// # Ok(())
/// # }
/// ```
pub fn get_area_tree(data: Vec<u8>) -> Result<NavTree, ParseError> {
let areas = read_areas(data)?;
@ -40,19 +58,60 @@ pub fn get_area_tree(data: Vec<u8>) -> Result<NavTree, ParseError> {
}
impl NavTree {
pub fn query(
&self,
x: f32,
y: f32,
) -> impl Iterator<Item = (&NavArea, TypedRect<f32, HammerUnit>, ItemId)> {
/// Find the navigation areas at a x/y cooordinate
///
/// ## Examples
///
/// ```no_run
/// use sourcenav::get_area_tree;
///
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let file = std::fs::read("path/to/navfile.nav")?;
/// let tree = get_area_tree(file)?;
/// let areas = tree.query(150.0, -312.0);
/// # Ok(())
/// # }
/// ```
pub fn query(&self, x: f32, y: f32) -> impl Iterator<Item = &NavArea> {
let query_box = TypedRect::new(TypedPoint2D::new(x, y), TypedSize2D::new(1.0, 1.0));
self.0.query(query_box).into_iter()
self.0.query(query_box).into_iter().map(|(area, ..)| area)
}
/// Find the z-height of a specfic x/y cooordinate
///
/// Note that multiple heights might exist for a given x/y coooridnate
///
/// ## Examples
///
/// ```no_run
/// use sourcenav::get_area_tree;
///
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let file = std::fs::read("path/to/navfile.nav")?;
/// let tree = get_area_tree(file)?;
/// let heights = tree.find_z_height(150.0, -312.0);
/// # Ok(())
/// # }
/// ```
pub fn find_z_height<'a>(&'a self, x: f32, y: f32) -> impl Iterator<Item = f32> + 'a {
self.query(x, y)
.map(move |(area, ..)| area.get_z_height(x, y))
self.query(x, y).map(move |area| area.get_z_height(x, y))
}
/// Get the z height at a point
///
/// A z-guess should be provided to resolve cases where multiple z values are possible
pub fn find_best_height(&self, x: f32, y: f32, z_guess: f32) -> f32 {
let found_heights = self.find_z_height(x, y);
let best_z = f32::MIN;
found_heights.fold(best_z, |best_z, found_z| {
if (found_z - z_guess).abs() < (best_z - z_guess).abs() {
found_z
} else {
best_z
}
})
}
}
@ -94,11 +153,10 @@ fn test_tree() {
);
assert_eq!(
tree.query(point3.0, point3.1)
.next()
.map(|(area, ..)| area.id),
tree.query(point4.0, point4.1)
.next()
.map(|(area, ..)| area.id)
tree.query(point3.0, point3.1).next().map(|area| area.id),
tree.query(point4.0, point4.1).next().map(|area| area.id)
);
}
#[cfg(doctest)]
doc_comment::doctest!("../README.md");

View file

@ -3,12 +3,18 @@ use bitbuffer::{BitRead, BitReadStream, Endianness, ReadError};
use euclid::{TypedPoint2D, TypedRect, TypedSize2D};
use std::ops::Index;
/// A 3 dimensional coordinate
#[derive(Debug, BitRead)]
pub struct Vector3(pub f32, pub f32, pub f32);
/// A unique identifier for a navigation area
#[derive(Debug, BitRead, Clone, Copy, Eq, PartialEq)]
pub struct NavAreaId(u32);
/// A navigation area from the nav file
#[derive(Debug)]
pub struct NavArea {
pub id: u32,
pub id: NavAreaId,
pub north_west: Vector3,
pub south_east: Vector3,
pub north_east_z: f32,
@ -35,6 +41,22 @@ impl NavArea {
self.south_east.1 - self.north_west.1
}
/// Get the z height of a x/y point inside the navigation area
///
/// # Examples
///
/// ```no_run
/// use sourcenav::get_area_tree;
///
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let file = std::fs::read("path/to/navfile.nav")?;
/// let tree = get_area_tree(file)?;
/// let area = tree.query(150.0, -312.0).next().unwrap();
///
/// let height = area.get_z_height(150.0, -312.0);
/// # Ok(())
/// # }
/// ```
pub fn get_z_height(&self, x: f32, y: f32) -> f32 {
let from_east = self.south_east.0 - x;
let from_south = self.south_east.0 - y;
@ -51,7 +73,7 @@ impl NavArea {
}
}
pub struct HammerUnit;
pub(crate) struct HammerUnit;
impl Spatial<HammerUnit> for NavArea {
fn aabb(&self) -> TypedRect<f32, HammerUnit> {
@ -65,8 +87,26 @@ impl Spatial<HammerUnit> for NavArea {
}
}
#[derive(Debug)]
pub struct Connections([Vec<u32>; 4]);
/// The connections from a navigation area into it's neighbours
///
/// Contains a list of area id's for every [`NavDirection`]
///
/// # Examples
///
/// ```no_run
/// # fn get_connections_from_somewhere() -> sourcenav::Connections {
/// # Default::default()
/// # }
/// use sourcenav::NavDirection;
///
/// let connections = get_connections_from_somewhere();
///
/// let north_connections = &connections[NavDirection::North];
/// ```
///
/// [`NavDirection`]: ./enum.NavDirection.html
#[derive(Debug, Default)]
pub struct Connections([Vec<NavAreaId>; 4]);
impl<E: Endianness> BitRead<E> for Connections {
fn read(stream: &mut BitReadStream<E>) -> Result<Self, ReadError> {
@ -85,15 +125,33 @@ impl<E: Endianness> BitRead<E> for Connections {
}
impl Index<NavDirection> for Connections {
type Output = Vec<u32>;
type Output = Vec<NavAreaId>;
fn index(&self, index: NavDirection) -> &Self::Output {
&self.0[index as u8 as usize]
}
}
#[derive(Debug)]
pub struct LadderConnections([Vec<u32>; 2]);
/// The connections from a navigation area into it's neighbours
///
/// Contains a list of area id's for every [`NavDirection`]
///
/// # Examples
///
/// ```no_run
/// # fn get_ladder_connections_from_somewhere() -> sourcenav::LadderConnections {
/// # Default::default()
/// # }
/// use sourcenav::LadderDirection;
///
/// let connections = get_ladder_connections_from_somewhere();
///
/// let down_connections = &connections[LadderDirection::Down];
/// ```
///
/// [`NavDirection`]: ./enum.NavDirection.html
#[derive(Debug, Default)]
pub struct LadderConnections([Vec<NavAreaId>; 2]);
impl<E: Endianness> BitRead<E> for LadderConnections {
fn read(stream: &mut BitReadStream<E>) -> Result<Self, ReadError> {
@ -112,13 +170,14 @@ impl<E: Endianness> BitRead<E> for LadderConnections {
}
impl Index<LadderDirection> for LadderConnections {
type Output = Vec<u32>;
type Output = Vec<NavAreaId>;
fn index(&self, index: LadderDirection) -> &Self::Output {
&self.0[index as u8 as usize]
}
}
/// The directions in which two areas can be connected
#[derive(Debug, BitRead)]
#[repr(u8)]
#[discriminant_bits = 8]
@ -129,6 +188,7 @@ pub enum NavDirection {
West,
}
/// The directions in which two areas can be connected by ladder
#[derive(Debug, BitRead)]
#[repr(u8)]
#[discriminant_bits = 8]
@ -137,6 +197,7 @@ pub enum LadderDirection {
Down,
}
/// A hiding spot within an area
#[derive(Debug, BitRead)]
pub struct NavHidingSpot {
id: u32,
@ -144,6 +205,7 @@ pub struct NavHidingSpot {
flags: u8,
}
/// An area that can be used for approach, no longer used in newer nav files
#[derive(Debug, BitRead)]
pub struct ApproachArea {
approach_here: u32,
@ -153,11 +215,12 @@ pub struct ApproachArea {
approach_how: u8,
}
/// A path that can be used to approach an area
#[derive(Debug, BitRead)]
pub struct EncounterPath {
from_area_id: u32,
from_area_id: NavAreaId,
from_direction: u8,
to_area_id: u32,
to_area_id: NavAreaId,
to_direction: u8,
#[size_bits = 8]
spots: Vec<EncounterSpot>,
@ -169,6 +232,7 @@ pub struct EncounterSpot {
distance: u8, // divide by 255
}
/// The light intensity at the four corners of an area
#[derive(Debug, BitRead, Default)]
pub struct LightIntensity {
pub north_west: f32,
@ -177,6 +241,7 @@ pub struct LightIntensity {
pub south_east: f32,
}
/// An area that is visible
#[derive(Debug, BitRead)]
pub struct VisibleArea {
id: u32,

View file

@ -2,20 +2,24 @@ pub use crate::navmesh::NavArea;
use bitbuffer::{BitReadBuffer, BitReadStream, LittleEndian};
use err_derive::Error;
/// Errors that can occur when parsing the binary nav file
#[derive(Debug, Error)]
pub enum ParseError {
/// An error ocured when reading from the source binary data
#[error(display = "Error while reading from data: {}", _0)]
ReadError(#[error(source)] bitbuffer::ReadError),
#[error(
display = "Invalid magic number ({:#8X}), not a nav file or corrupted",
_0
)]
/// The binary data contained an invalid magic number and is probably not a nav file
InvalidMagicNumber(u32),
/// The version of the nav file is not supported by this parser
#[error(display = "The major version for this nav ({}), is not supported", _0)]
UnsupportedVersion(u32),
}
pub fn read_areas(data: Vec<u8>) -> Result<Vec<NavArea>, ParseError> {
pub(crate) fn read_areas(data: Vec<u8>) -> Result<Vec<NavArea>, ParseError> {
let mut data = BitReadStream::new(BitReadBuffer::new(data, LittleEndian));
let magic = data.read()?;
if magic != 0xFEEDFACE {