Skip to content

Commit

Permalink
docs and fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
ohchase committed Apr 20, 2024
1 parent 665907c commit 444b779
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 146 deletions.
18 changes: 8 additions & 10 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,22 @@ name = "divert"
version = "0.5.0"
edition = "2021"
description = "Rust Lang bindings for Recast Navigation"
homepage = "https://github.com/0xFounders/divert"
repository = "https://github.com/0xFounders/divert"
homepage = "https://github.com/ohchase/divert"
repository = "https://github.com/ohchase/divert"
license = "Zlib"

[lib]
crate-type = ["lib"]

[dependencies]
bitflags = "1.3"
thiserror = "1.0.31"
log = "0.4.17"
recastnavigation-sys = {version = "1.0.1", features = ["detour_large_nav_meshes"]}
bitflags = "2.5.0"
thiserror = "1.0.58"
recastnavigation-sys = { version = "1.0.3", features = [
"detour_large_nav_meshes",
] }

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dev-dependencies]
byteorder = "1.4.3"
log = "0.4.16"
pretty_env_logger = "0.4.0"
lazy_static = "1.4.0"
thiserror = "1.0.37"
rand = "0.8.5"
rand = "0.8.5"
23 changes: 3 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,31 +1,21 @@
[![Actions Status](https://github.com/0xFounders/divert/workflows/Continuous%20integration/badge.svg)](https://github.com/0xFounders/divert/actions)
[![Actions Status](https://github.com/ohchase/divert/workflows/Continuous%20integration/badge.svg)](https://github.com/ohchase/divert/actions)
[![Crate](https://img.shields.io/crates/v/divert.svg)](https://crates.io/crates/divert)

# Divert
Rust bindings for [Recast Navigation](https://github.com/recastnavigation/recastnavigation).


## Purpose
Provide safe bindings to [Recast Navigation](https://github.com/recastnavigation/recastnavigation) to allow for 3d navigation in a rust application.

## Overview
### `src/extern.cpp`
C function definitions wrapping Detour C++ Functions.
### `src/binding.rs`
Rust bindings to the C functions exposed in `src/extern.cpp`.
### `src/lib.rs`
Safe Rust abstractions of Detour components e.g ensuring correct freeing of DtNavMesh and DtNavMeshQuery.

## How to Build
```
git clone --recurse-submodules https://github.com/0xFounders/divert.git
git clone --recurse-submodules https://github.com/ohchase/divert.git
cargo build
```

## Use Case
Refer to `examples/pathfinding.rs` for a demonstration of loading geometry generated with [Trinity Core](https://github.com/TrinityCore/TrinityCore). In the below, Proof of Concept, section the paths generated are projected to in-game space. In this repository the resources for generating paths is provided, but drawing/projecting points in the game is not in scope of this project. No questions or issues should be opened requesting help or information about video game specific applications.


```
cargo run --example path_finding
Compiling divert v0.1.0 (...\divert)
Expand All @@ -39,11 +29,4 @@ cargo run --example path_finding
INFO path_finding > DtVector { y: 4352.9404, z: 3.0786226, x: -2051.5564 }
INFO path_finding > DtVector { y: 4354.9106, z: 3.0870965, x: -2051.213 }
INFO path_finding > DtVector { y: 4356.881, z: 2.9974942, x: -2050.8694 }
```

## Proof Of Concept
Demonstration of my independent work using this library to generate navigation paths in a third party video game.

![Navigation Demo 1](resources/docs/demo_nav.PNG)

![Navigation Demo 2](resources/docs/demo_nav_2.PNG)
```
97 changes: 18 additions & 79 deletions examples/path_finding.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
use byteorder::{LittleEndian, ReadBytesExt};
use log::{info, LevelFilter};

use divert::{
DivertError, DivertResult, DtStraightPathFlags, NavMesh, NavMeshParams, NavMeshQuery, PolyRef,
QueryFilter, TileRef, Vector,
};
use rand::Rng;
use thiserror::Error;

use std::{
Expand All @@ -14,20 +11,13 @@ use std::{
error::Error,
fs::File,
io::{self, Read, Seek, SeekFrom},
sync::Mutex,
};

trait DataProvider: Send + Sync {
fn read_map_params(&self) -> io::Result<NavMeshParams>;

fn read_tile_data(&self, tile_x: &i32, tile_y: &i32) -> io::Result<Vec<u8>>;
}

struct TrinityDataProvider {
map_id: i32,
}

impl DataProvider for TrinityDataProvider {
impl TrinityDataProvider {
fn read_map_params(&self) -> io::Result<NavMeshParams> {
let mut map_params_file =
File::open(format!("resources/geometry/{:03}.mmap", self.map_id))?;
Expand Down Expand Up @@ -90,12 +80,12 @@ impl Default for PathFindingSettings {

struct TrinityNavEngine {
nav_mesh: NavMesh,
data_provider: Box<dyn DataProvider>,
data_provider: TrinityDataProvider,
loaded_tiles: HashSet<u32>,
}

impl TrinityNavEngine {
pub fn new(data_provider: Box<dyn DataProvider>) -> Result<Self, Box<dyn Error>> {
pub fn new(data_provider: TrinityDataProvider) -> Result<Self, Box<dyn Error>> {
let map_params = data_provider.read_map_params()?;
let nav_mesh = NavMesh::new(&map_params)?;

Expand All @@ -121,7 +111,7 @@ impl TrinityNavEngine {

pub fn add_tile(&mut self, tile_x: &i32, tile_y: &i32) -> DivertResult<TileRef> {
if self.has_tile(tile_x, tile_y) {
info!(
println!(
"[NavEngine] Add tile called for already loaded tile ({}, {})",
tile_x, tile_y
);
Expand All @@ -130,7 +120,7 @@ impl TrinityNavEngine {
return Ok(TileRef::default());
}

info!("[NavEngine] Adding tile ({}, {})", tile_x, tile_y);
println!("[NavEngine] Adding tile ({}, {})", tile_x, tile_y);
let tile_ref = self
.nav_mesh
.add_tile(self.data_provider.read_tile_data(tile_x, tile_y).unwrap())?;
Expand All @@ -140,12 +130,6 @@ impl TrinityNavEngine {
}
}

lazy_static::lazy_static! {
static ref NAV_ENGINE: Mutex<TrinityNavEngine> = Mutex::new(
TrinityNavEngine::new(Box::new(TrinityDataProvider { map_id: 530 }))
.expect("Unable to initialize global navigation engine, example resources not available."));
}

#[derive(Error, Debug)]
enum NavigationError {
#[error("Divert related error")]
Expand All @@ -159,20 +143,14 @@ enum NavigationError {
}

type NavigationResult<T> = std::result::Result<T, NavigationError>;

struct TrinityNavigator<'a> {
query: NavMeshQuery<'a>,
query_filter: QueryFilter,
settings: PathFindingSettings,
}

impl<'a> TrinityNavigator<'a> {
fn new(query_filter: QueryFilter) -> DivertResult<Self> {
let nav_engine = NAV_ENGINE
.lock()
.expect("Global Nav Engine Mutex has been poisoned");
let query = nav_engine.create_query(2048)?;

fn initialize(query: NavMeshQuery<'a>, query_filter: QueryFilter) -> DivertResult<Self> {
Ok(Self {
query,
query_filter,
Expand Down Expand Up @@ -286,6 +264,7 @@ impl<'a> TrinityNavigator<'a> {
Ok(None)
}

/// Try to find a smooth path with available tiles
fn find_smooth_path(
&self,
start_pos: &Vector,
Expand Down Expand Up @@ -357,27 +336,17 @@ impl<'a> TrinityNavigator<'a> {
Ok(smooth_path)
}

/// Try to find a path without loading any tiles
pub fn find_path(
&self,
input_start: &Vector,
input_end: &Vector,
) -> NavigationResult<Vec<Vector>> {
log::info!(
println!(
"[TrinityNavigator] Generating path from {:?} to {:?}",
input_start,
input_end
input_start, input_end
);

{
let nav_engine = &mut NAV_ENGINE
.lock()
.expect("Global Nav Engine Mutex has been poisoned");
let start_tile = Self::world_to_trinity(input_start.x, input_start.y);
let end_tile = Self::world_to_trinity(input_end.x, input_end.y);
nav_engine.add_tile(&start_tile.0, &start_tile.1)?;
nav_engine.add_tile(&end_tile.0, &end_tile.1)?;
}

let (start_poly, start_pos) = self.find_nearest_poly(input_start)?;
let (end_poly, end_pos) = self.find_nearest_poly(input_end)?;

Expand All @@ -391,7 +360,7 @@ impl<'a> TrinityNavigator<'a> {
)?;

let smooth_path = self.find_smooth_path(&start_pos, &end_pos, poly_path)?;
log::info!(
println!(
"[TrinityNavigator] Successfully generated path of len: {}",
smooth_path.len()
);
Expand All @@ -401,15 +370,13 @@ impl<'a> TrinityNavigator<'a> {
}

fn main() -> Result<(), Box<dyn Error>> {
pretty_env_logger::formatted_builder()
.filter_level(LevelFilter::Info)
.init();

let mut query_filter = QueryFilter::new();
query_filter.set_include_flags(1 | 8 | 4 | 2);
query_filter.set_exclude_flags(0);

let navigator = TrinityNavigator::new(query_filter)?;
let mut nav_engine = TrinityNavEngine::new(TrinityDataProvider { map_id: 530 })?;
let nav_mesh_query = nav_engine.create_query(2048)?;
let navigator = TrinityNavigator::initialize(nav_mesh_query, query_filter)?;

// Simple path
// Shat Bridge (35,22) -> (35, 22)
Expand All @@ -429,39 +396,11 @@ fn main() -> Result<(), Box<dyn Error>> {
// Terrokar (35,22) -> (35, 23)
let start_position = Vector::from_xyz(-2051.9, 4350.97, 2.25);
let end_position = Vector::from_xyz(-1916.12, 4894.67, 2.21);
let start_tile = TrinityNavigator::world_to_trinity(start_position.x, start_position.y);
let end_tile = TrinityNavigator::world_to_trinity(end_position.x, end_position.y);
nav_engine.add_tile(&start_tile.0, &start_tile.1)?;
nav_engine.add_tile(&end_tile.0, &end_tile.1)?;
navigator.find_path(&start_position, &end_position)?;
// path.iter().for_each(|position| info!("{:?}", position));
// Terrokar

extern "C" fn frand() -> f32 {
let mut rng = rand::thread_rng();
rng.gen::<f32>()
}

match navigator
.query
.find_random_point(frand, &navigator.query_filter)
{
Ok((poly_ref, pos)) => {
log::info!("Random Position: {:?} {:?}", poly_ref, pos);

let points = navigator.query.find_polys_around_circle(
poly_ref,
&pos,
25.0,
&navigator.query_filter,
256,
)?;

for (poly_ref, parent_ref, cost) in points.iter() {
log::info!("Poly {:?} {:?} {}", poly_ref, parent_ref, cost);
}
}
Err(err) => log::warn!("Random Position Err: {:#?}", err),
};

// Cross zones
// ...

Ok(())
}
Loading

0 comments on commit 444b779

Please sign in to comment.