use super::components::NavMeshAgentState;
use crate::ecs::world::{
BOUNDING_VOLUME, GLOBAL_TRANSFORM, LOCAL_TRANSFORM, LOCAL_TRANSFORM_DIRTY, MATERIAL_REF, NAME,
NAVMESH_AGENT, RENDER_MESH, VISIBILITY, World,
};
use nalgebra_glm::Vec3;
pub fn spawn_navmesh_agent(world: &mut World, position: Vec3, speed: f32) -> freecs::Entity {
let entity = world.spawn_entities(
NAME | LOCAL_TRANSFORM
| GLOBAL_TRANSFORM
| LOCAL_TRANSFORM_DIRTY
| RENDER_MESH
| MATERIAL_REF
| BOUNDING_VOLUME
| VISIBILITY
| NAVMESH_AGENT,
1,
)[0];
if let Some(name) = world.core.get_name_mut(entity) {
name.0 = "NavMesh Agent".to_string();
}
if let Some(transform) = world.core.get_local_transform_mut(entity) {
transform.translation = position;
transform.scale = nalgebra_glm::vec3(0.4, 0.8, 0.4);
}
if let Some(mesh) = world.core.get_render_mesh_mut(entity) {
mesh.name = "Capsule".to_string();
}
if let Some(agent) = world.core.get_navmesh_agent_mut(entity) {
agent.movement_speed = speed;
}
if let Some(bounding_volume) = world.core.get_bounding_volume_mut(entity) {
*bounding_volume =
crate::ecs::bounding_volume::components::BoundingVolume::from_mesh_type("Capsule");
}
if let Some(visibility) = world.core.get_visibility_mut(entity) {
visibility.visible = true;
}
entity
}
pub fn set_agent_destination(world: &mut World, entity: freecs::Entity, destination: Vec3) {
if let Some(agent) = world.core.get_navmesh_agent_mut(entity) {
agent.target_position = Some(destination);
agent.state = NavMeshAgentState::PathPending;
}
}
pub fn stop_agent(world: &mut World, entity: freecs::Entity) {
if let Some(agent) = world.core.get_navmesh_agent_mut(entity) {
agent.clear_destination();
}
}
pub fn set_agent_speed(world: &mut World, entity: freecs::Entity, speed: f32) {
if let Some(agent) = world.core.get_navmesh_agent_mut(entity) {
agent.movement_speed = speed;
}
}
pub fn clear_navmesh(world: &mut World) {
world.resources.navmesh.clear();
}
pub fn set_navmesh_debug_draw(world: &mut World, enabled: bool) {
world.resources.navmesh.debug_draw = enabled;
}
pub fn get_agent_state(world: &World, entity: freecs::Entity) -> Option<NavMeshAgentState> {
world
.core
.get_navmesh_agent(entity)
.map(|agent| agent.state)
}
pub fn get_agent_path_length(world: &World, entity: freecs::Entity) -> Option<usize> {
world
.core
.get_navmesh_agent(entity)
.map(|agent| agent.remaining_waypoints())
}
#[cfg(all(feature = "physics", feature = "navmesh"))]
pub fn generate_navmesh_from_world(
world: &mut World,
config: &super::generation::RecastNavMeshConfig,
) {
let mut vertices: Vec<[f32; 3]> = Vec::new();
let mut indices: Vec<[u32; 3]> = Vec::new();
for (_, collider) in world.resources.physics.collider_set.iter() {
let parent_handle = collider.parent();
let is_static = if let Some(parent_handle) = parent_handle {
world
.resources
.physics
.rigid_body_set
.get(parent_handle)
.is_some_and(|rb| !rb.is_dynamic())
} else {
true
};
if !is_static {
continue;
}
let position = collider.position();
let shape = collider.shape();
if let Some(trimesh) = shape.as_trimesh() {
let base_index = vertices.len() as u32;
for vertex in trimesh.vertices() {
let world_vertex = position * vertex;
vertices.push([world_vertex.x, world_vertex.y, world_vertex.z]);
}
for triangle in trimesh.indices() {
indices.push([
base_index + triangle[0],
base_index + triangle[1],
base_index + triangle[2],
]);
}
} else if let Some(cuboid) = shape.as_cuboid() {
let half = cuboid.half_extents;
let base_index = vertices.len() as u32;
let corners = [
rapier3d::na::Point3::new(-half.x, -half.y, -half.z),
rapier3d::na::Point3::new(half.x, -half.y, -half.z),
rapier3d::na::Point3::new(half.x, half.y, -half.z),
rapier3d::na::Point3::new(-half.x, half.y, -half.z),
rapier3d::na::Point3::new(-half.x, -half.y, half.z),
rapier3d::na::Point3::new(half.x, -half.y, half.z),
rapier3d::na::Point3::new(half.x, half.y, half.z),
rapier3d::na::Point3::new(-half.x, half.y, half.z),
];
for corner in &corners {
let world_vertex = position * corner;
vertices.push([world_vertex.x, world_vertex.y, world_vertex.z]);
}
let cube_triangles: [[u32; 3]; 12] = [
[0, 1, 2],
[0, 2, 3],
[4, 6, 5],
[4, 7, 6],
[0, 4, 5],
[0, 5, 1],
[2, 6, 7],
[2, 7, 3],
[0, 3, 7],
[0, 7, 4],
[1, 5, 6],
[1, 6, 2],
];
for triangle in &cube_triangles {
indices.push([
base_index + triangle[0],
base_index + triangle[1],
base_index + triangle[2],
]);
}
}
}
if vertices.is_empty() || indices.is_empty() {
return;
}
if let Some(navmesh) = super::generation::generate_navmesh_recast(&vertices, &indices, config) {
world.resources.navmesh = navmesh;
}
}