landmass_rerecast

Crates.iolandmass_rerecast
lib.rslandmass_rerecast
version0.1.0
created_at2025-10-03 03:24:33.38765+00
updated_at2025-10-03 03:24:33.38765+00
descriptionAn integration to allow use of rerecast navigation meshes in landmass.
homepage
repositoryhttps://github.com/andriyDev/landmass
max_upload_size
id1866040
size840,255
(andriyDev)

documentation

README

landmass_rerecast

A plugin for Bevy to help using bevy_landmass and bevy_rerecast together.

Overview

landmass_rerecast allows you to use bevy_rerecast::Navmesh as the navigation mesh in a landmass island! To use it:

  1. Setup bevy_landmass and bevy_rerecast up as normal.
  2. Add the LandmassRerecastPlugin to your app.
  3. Replace uses of bevy_landmass::Island3dBundle, bevy_landmass::NavMeshHandle3d, and bevy_landmass::NavMeshHandle, with landmass_rerecast::Island3dBundle, landmass_rerecast::NavMeshHandle3d, and landmass_rerecast::NavMeshHandle, respectively.
    1. Note: you can either change the imports, or if you are importing the bevy_landmass prelude, you can just import these symbols manually.
    2. These types are also designed to require minimal changes from you, so it may just be as simple as adding these imports!
  4. Use bevy_rerecast::Navmesh handles in your landmass_rerecast::NavMeshHandle3ds!

Example

use bevy::prelude::*;
use bevy_landmass::{Agent, PointSampleDistance3d, prelude::*};
use bevy_rerecast::{Mesh3dBackendPlugin, prelude::*};
use landmass_rerecast::LandmassRerecastPlugin;
use landmass_rerecast::{Island3dBundle, NavMeshHandle3d};

fn main() {
  App::new()
    .add_plugins((
      MinimalPlugins,
      AssetPlugin::default(),
      TransformPlugin,
      NavmeshPlugins::default(),
      Mesh3dBackendPlugin::default(),
      Landmass3dPlugin::default(),
      LandmassRerecastPlugin::default(),
    ))
    .init_asset::<Mesh>()
    .init_asset::<StandardMaterial>()
    .add_systems(Startup, setup)
    .add_systems(
      Update,
      check_for_path_and_exit.run_if(resource_exists::<CheckCounter>),
    )
    .add_observer(check_after_navmesh_ready)
    .run();
}

fn setup(
  mut commands: Commands,
  mut meshes: ResMut<Assets<Mesh>>,
  mut materials: ResMut<Assets<StandardMaterial>>,
  mut generator: NavmeshGenerator,
) {
  commands.spawn((
    Mesh3d(meshes.add(Cuboid::new(20.0, 1.0, 20.0))),
    MeshMaterial3d(materials.add(StandardMaterial {
      base_color: Srgba::WHITE.into(),
      ..Default::default()
    })),
  ));

  let archipelago = commands
    .spawn(Archipelago3d::new(ArchipelagoOptions {
      point_sample_distance: PointSampleDistance3d {
        distance_above: 0.5,
        distance_below: 0.5,
        ..PointSampleDistance3d::from_agent_radius(0.5)
      },
      ..ArchipelagoOptions::from_agent_radius(0.5)
    }))
    .id();

  // This island's nav mesh will be generated by `bevy_rerecast`!
  commands.spawn(Island3dBundle {
    island: Island,
    archipelago_ref: ArchipelagoRef3d::new(archipelago),
    nav_mesh: NavMeshHandle3d(
      generator
        .generate(NavmeshSettings { agent_radius: 0.5, ..Default::default() }),
    ),
  });

  // Create an agent that will find a path as soon as the nav mesh is generated.
  commands.spawn((
    Agent3dBundle {
      agent: Agent::default(),
      archipelago_ref: ArchipelagoRef3d::new(archipelago),
      settings: AgentSettings {
        desired_speed: 5.0,
        max_speed: 10.0,
        radius: 0.5,
      },
    },
    Transform::from_xyz(-5.0, 0.5, -5.0),
    AgentTarget3d::Point(Vec3::new(5.0, 0.5, 5.0)),
  ));
}

// All the stuff below here is just to allow the doc tests to pass.

fn check_after_navmesh_ready(_: Trigger<NavmeshReady>, mut commands: Commands) {
  // Allow checking three times before failure, in case we need an extra frame
  // for the meshes to propagate.
  commands.insert_resource(CheckCounter(3));
}

#[derive(Resource)]
struct CheckCounter(usize);

fn check_for_path_and_exit(
  agent: Single<(&AgentState, &AgentDesiredVelocity3d)>,
  mut check_counter: ResMut<CheckCounter>,
  mut exit: EventWriter<AppExit>,
) {
  let (state, desired_velocity) = *agent;

  let expected_velocity = Vec3::new(1.0, 0.0, 1.0).normalize() * 5.0;
  if *state == AgentState::Moving
    && desired_velocity.velocity().abs_diff_eq(expected_velocity, 1e-3)
  {
    exit.write(AppExit::Success);
    return;
  }

  check_counter.0 -= 1;
  if check_counter.0 == 0 {
    panic!("Did not find expected path! state={state:?}, desired_velocity={desired_velocity:?}");
  }
}

License

License under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Commit count: 412

cargo fmt