Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs and fixes #7

Merged
merged 2 commits into from
Apr 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 9 additions & 11 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,26 +1,24 @@
[package]
name = "divert"
version = "0.5.0"
version = "0.6.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
Loading