Rust物理引擎库ncollide3d的使用,ncollide3d提供高效3D碰撞检测和几何计算功能

Rust物理引擎库ncollide3d的使用,ncollide3d提供高效3D碰撞检测和几何计算功能

ncollide是一个用Rust编写的2D和3D碰撞检测库,其中ncollide3d专门用于3维碰撞检测。

主要特性

  • 基于动态边界体积树的广泛阶段检测
  • 球体与球体的碰撞检测
  • 平面与任何凸面物体的碰撞检测
  • 任意凸面物体之间的碰撞检测
  • 复合几何体
  • 射线投射
  • 无旋转运动物体的碰撞时间计算(复合体与复合体之间的碰撞尚未实现)

安装

在Cargo.toml中添加以下依赖:

[dependencies]
ncollide3d = "0.33" # 用于3D碰撞检测

或者运行命令:

cargo add ncollide3d

示例代码

以下是使用ncollide3d进行基本碰撞检测的示例:

use ncollide3d::shape::{Cuboid, Ball};
use ncollide3d::query::{self, Proximity};
use nalgebra::{Vector3, Isometry3};

fn main() {
    // 创建两个形状:一个立方体和一个球体
    let cube = Cuboid::new(Vector3::new(1.0, 1.0, 1.0));
    let ball = Ball::new(1.0);
    
    // 创建它们的位置和方向
    let cube_pos = Isometry3::identity();
    let ball_pos = Isometry3::translation(1.5, 0.0, 0.0);
    
    // 检测它们之间的接近程度
    let proximity = query::proximity(&cube_pos, &cube, &ball_pos, &ball, 0.0);
    
    match proximity {
        Proximity::Disjoint => println!("物体没有接触"),
        Proximity::Intersecting => println!("物体相交"),
        Proximity::WithinMargin => println!("物体接近但未接触"),
    }
    
    // 检测它们是否相交
    let contacts = query::contact(&cube_pos, &cube, &ball_pos, &ball, 0.0);
    
    if let Some(contact) = contacts {
        println!("物体相交于点: {:?}", contact.point);
        println!("穿透深度: {}", contact.depth);
    } else {
        println!("物体没有相交");
    }
}

射线投射示例

use ncollide3d::shape::{Cuboid, Ball};
use ncollide3d::query::Ray;
use nalgebra::{Vector3, Point3, Isometry3};

fn main() {
    // 创建一个立方体
    let cube = Cuboid::new(Vector3::new(1.0, 1.0, 1.0));
    let cube_pos = Isometry3::identity();
    
    // 创建一条射线
    let ray = Ray::new(
        Point3::new(0.0, 0.0, -5.0),  // 起点
        Vector3::new(0.0, 0.0, 1.0)   // 方向
    );
    
    // 检测射线与立方体的交点
    if let Some(hit) = query::ray_toi(&cube_pos, &cube, &ray, true) {
        println!("射线在距离 {} 处击中立方体", hit);
    } else {
        println!("射线没有击中立方体");
    }
}

完整示例

下面是一个更完整的ncollide3d使用示例,演示了多种碰撞检测功能:

use ncollide3d::shape::{Cuboid, Ball, Plane};
use ncollide3d::query::{self, Proximity, Ray, RayCast};
use nalgebra::{Vector3, Point3, Isometry3};

fn main() {
    // 1. 立方体和球体的碰撞检测
    println!("=== 立方体和球体碰撞检测 ===");
    let cube = Cuboid::new(Vector3::new(1.0, 1.0, 1.0));
    let ball = Ball::new(0.8);
    
    let cube_pos = Isometry3::identity();
    let ball_pos = Isometry3::translation(1.5, 0.0, 0.0);
    
    let proximity = query::proximity(&cube_pos, &cube, &ball_pos, &ball, 0.0);
    match proximity {
        Proximity::Disjoint => println!("立方体和球体没有接触"),
        Proximity::Intersecting => println!("立方体和球体相交"),
        Proximity::WithinMargin => println!("立方体和球体接近但未接触"),
    }
    
    // 2. 平面和球体的碰撞检测
    println!("\n=== 平面和球体碰撞检测 ===");
    let plane = Plane::new(Vector3::new(0.0, 1.0, 0.0));
    let plane_pos = Isometry3::identity();
    
    let ball_on_plane = Ball::new(0.5);
    let ball_pos_above = Isometry3::translation(0.0, 0.4, 0.0);
    let ball_pos_below = Isometry3::translation(0.0, -0.6, 0.0);
    
    println!("球体在平面上方:");
    match query::proximity(&plane_pos, &plane, &ball_pos_above, &ball_on_plane, 0.0) {
        Proximity::Intersecting => println!("球体与平面相交"),
        _ => println!("球体与平面不相交"),
    }
    
    println!("球体在平面下方:");
    match query::proximity(&plane_pos, &plane, &ball_pos_below, &ball_on_plane, 0.0) {
        Proximity::Intersecting => println!("球体与平面相交"),
        _ => println!("球体与平面不相交"),
    }
    
    // 3. 射线投射高级用法
    println!("\n=== 射线投射测试 ===");
    let ray = Ray::new(
        Point3::new(0.0, 0.0, -5.0),  // 起点
        Vector3::new(0.0, 0.0, 1.0)   // 方向
    );
    
    println!("测试射线与立方体的交点:");
    if let Some(toi) = cube.toi_with_ray(&cube_pos, &ray, f32::MAX, true) {
        println!("射线在距离 {} 处击中立方体", toi);
        let hit_point = ray.point_at(toi);
        println!("击中点坐标: {:?}", hit_point);
    } else {
        println!("射线没有击中立方体");
    }
    
    // 4. 接触点计算
    println!("\n=== 接触点计算 ===");
    let small_cube = Cuboid::new(Vector3::new(0.5, 0.5, 0.5));
    let small_cube_pos = Isometry3::translation(0.7, 0.0, 0.0);
    
    if let Some(contact) = query::contact(&cube_pos, &cube, &small_cube_pos, &small_cube, 0.0) {
        println!("立方体相交于点: {:?}", contact.point);
        println!("穿透深度: {}", contact.depth);
        println!("接触法线: {:?}", contact.normal);
    } else {
        println!("立方体没有相交");
    }
}

请注意,ncollide现在处于被动维护状态,正在被Parry项目取代。


1 回复

Rust物理引擎库ncollide3d的使用指南

ncollide3d是Rust中一个高效的3D碰撞检测和几何计算库,专注于提供精确的碰撞检测和物理模拟功能。

主要特性

  • 支持多种几何形状的碰撞检测
  • 提供高效的边界体积层次结构(BVH)加速
  • 包含多种几何查询功能(射线投射、点包含测试等)
  • 支持凸多面体和复合形状
  • 与nphysics等物理引擎良好集成

基本使用方法

添加依赖

首先在Cargo.toml中添加依赖:

[dependencies]
ncollide3d = "0.30"
nalgebra = "0.30"  # 需要配合线性代数库使用

基本碰撞检测示例

use ncollide3d::shape::{Ball, Cuboid};
use ncollide3d::query::contacts_internal;
use nalgebra::{Point3, Vector3};

fn main() {
    // 创建一个球体(半径1.0)
    let ball = Ball::new(1.0);
    
    // 创建一个立方体(半边长分别为1.0, 2.0, 1.0)
    let cuboid = Cuboid::new(Vector3::new(1.0, 2.0, 1.0));
    
    // 定义球体的位置和方向
    let ball_pos = Point3::new(1.0, 0.0, 极0.0);
    let ball_rot = Vector3::zeros(); // 无旋转
    
    // 定义立方体的位置和方向
    let cuboid_pos = Point3::new(3.0, 0.0, 0.0);
    let cuboid_rot = Vector3::zeros(); // 无旋转
    
    // 检测碰撞
    let prediction_distance = 0.0;
    let contact = contacts_internal::contact_support_map_support_map(
        &ball_pos,
        &ball_rot,
        &ball,
        &cuboid_pos,
        &cuboid_rot,
        &cuboid,
        prediction_distance,
    );
    
    if let Some(contact) = contact {
        println!("碰撞发生!穿透深度: {}", contact.depth);
    } else {
        println!("没有碰撞");
    }
}

射线投射示例

use ncollide3d::shape::{Cuboid, Shape};
use ncollide3d::query::Ray;
use nalgebra::{Point3, Vector3, Isometry3};

fn ray_cast_example() {
    // 创建一个立方体
    let cuboid = Cuboid::new(Vector3::new(1.0, 1.0, 1.0));
    
    // 立方体的位置和方向(单位矩阵表示无旋转)
    let cuboid_pos = Isometry3::identity();
    
    // 创建一条射线
    let ray_origin = Point3::new(0.0, 0.0, 5.0);
    let ray_direction = Vector3::new(0.0, 0.0, -1.0);
    let ray = Ray::new(ray_origin, ray_direction);
    
    // 最大投射距离
    let max_toi = 10.0;
    
    // 执行射线投射
    if let Some(hit) = cuboid.as_ray_cast().unwrap().to极i_and_normal_with_ray(
        &cuboid_pos,
        &ray,
        max_toi,
        true
    ) {
        println!("射线击中立方体!");
        println!("击中点: {:?}", ray.point_at(hit.toi));
        println!("法线: {:?}", hit.normal);
    } else {
        println!("射线未击中立方体");
    }
}

高级用法

复合形状(Compound Shape)

use ncollide3d::shape::{Compound, Cuboid, ShapeHandle};
use ncollide3d::bounding_volume::AABB;
use nalgebra::{Point3, Vector3, Isometry3};

fn compound_shape_example() {
    // 创建两个立方体
    let cuboid1 = ShapeHandle::new(Cuboid::new(Vector3::new(1.0, 1.0, 1.0)));
    let cuboid2 = ShapeHandle::new(Cuboid::new(Vector3::new(0.5, 0.5, 0.5)));
    
    // 定义它们的位置
    let pos1 = Isometry3::translation(1.0, 0.0, 0.0);
    let pos2 = Isometry3::translation(-1.0, 0.0, 0.0);
    
    // 创建复合形状
    let shapes = vec![(pos1, cuboid1), (pos2, cuboid2)];
    let compound = Compound::new(shapes);
    
    // 计算复合形状的AABB
    let compound_pos = Isometry3::identity();
    let aabb = compound.aabb(&compound_pos);
    println!("复合形状的AABB: {:?}", aabb);
}

碰撞世界(World)管理

对于更复杂的场景,可以使用ncollide3d的世界管理功能:

use ncollide3d::world::{CollisionWorld, GeometricQueryType};
use ncollide3d::shape::{Cuboid, ShapeHandle};
use nalgebra::{Point3, Isometry3};

fn collision_world_example() {
    // 创建碰撞世界
    let mut world = CollisionWorld::new(0.02);
    
    // 创建一些形状
    let shape = ShapeHandle::new(Cuboid::new(Vector3::new(1.0, 1.0, 1.0)));
    
    // 添加物体到世界
    let handle1 = world.add(
        Isometry3::translation(0.0, 0.0, 0.0),
        shape.clone(),
        GeometricQueryType::Contacts(0.0),
        (),
    );
    
    let handle2 = world.add(
        Isometry3::translation(2.0, 0.0, 0.0),
        shape,
        GeometricQueryType::Contacts(0.0),
        (),
    );
    
    // 更新世界(检测碰撞)
    world.update();
    
    // 获取碰撞对
    for collision in world.contact_pairs() {
        println!("物体 {:?} 和 {:?} 发生碰撞", collision.0, collision.1);
    }
}

性能提示

  1. 对于静态几何体,使用CollisionObject::set_prediction设置较大的预测距离可以减少更新频率
  2. 对于大量物体,考虑使用BroadPhase进行粗检测优化
  3. 重复使用的形状应该使用ShapeHandle来避免重复分配内存

ncollide3d是Rust生态中功能强大且高效的3D碰撞检测库,特别适合游戏开发和物理模拟应用场景。

完整示例Demo

下面是一个整合了上述功能的完整示例,展示了一个简单的物理场景:

use ncollide3d::{
    shape::{Ball, Cuboid, Compound, ShapeHandle},
    query::{Ray, contacts_internal},
    world::{CollisionWorld, GeometricQueryType},
    bounding_volume::AABB
};
use nalgebra::{Point3, Vector3, Isometry3};

fn main() {
    // 1. 基本碰撞检测
    basic_collision_detection();
    
    // 2. 射线投射
    ray_casting_example();
    
    // 3. 复合形状
    compound_shape_demo();
    
    // 4. 碰撞世界
    collision_world_demo();
}

fn basic_collision_detection() {
    println!("\n=== 基本碰撞检测 ===");
    
    let ball = Ball::new(1.0);
    let cuboid = Cuboid::new(Vector3::new(1.0, 2.0, 1.0));
    
    // 球体位置(会与立方体碰撞)
    let ball_pos = Point3::new(1.5, 0.0, 0.0);
    // 立方体位置
    let cuboid_pos = Point3::new(3.0, 0.0, 0.0);
    
    let contact = contacts_internal::contact_support_map_support_map(
        &ball_pos,
        &Vector3::zeros(),
        &ball,
        &cuboid_pos,
        &Vector3::zeros(),
        &cuboid,
        0.0,
    );
    
    match contact {
        Some(c) => println!("碰撞检测结果: 穿透深度 {:.2}", c.depth),
        None => println!("碰撞检测结果: 无碰撞"),
    }
}

fn ray_casting_example() {
    println!("\n=== 射线投射 ===");
    
    let cuboid = Cuboid::new(Vector3::new(1.0, 1.0, 1.0));
    let cuboid_pos = Isometry3::identity();
    
    // 从上方射向立方体的射线
    let ray = Ray::new(Point3::new(0.5, 0.5, 5.0), Vector3::new(0.0, 0.0, -1.0));
    
    if let Some(hit) = cuboid.as_ray_cast().unwrap().toi_and_normal_with_ray(
        &cuboid_pos,
        &ray,
        10.0,
        true
    ) {
        println!("射线击中立方体!");
        println!("击中点: {:?}", ray.point_at(hit.toi));
        println!("法向量: {:?}", hit.normal);
    } else {
        println!("射线未击中立方体");
    }
}

fn compound_shape_demo() {
    println!("\n=== 复合形状 ===");
    
    // 创建两个形状
    let box1 = ShapeHandle::new(Cuboid::new(Vector3::new(1.0, 1.0, 1.0)));
    let box2 = ShapeHandle::new(Ball::new(1.5));
    
    // 定义它们的位置
    let pos1 = Isometry3::translation(2.0, 0.0, 0.0);
    let pos2 = Isometry3::translation(-2.0, 0.0, 0.0);
    
    // 创建复合形状
    let compound = Compound::new(vec![(pos1, box1), (pos2, box2)]);
    
    // 计算AABB
    let aabb = compound.aabb(&Isometry3::identity());
    println!("复合形状的AABB: 最小点 {:?}, 最大点 {:?}", aabb.mins, aabb.maxs);
}

fn collision_world_demo() {
    println!("\n=== 碰撞世界 ===");
    
    let mut world = CollisionWorld::new(0.02);
    
    // 创建几个形状
    let ball = ShapeHandle::new(Ball::new(1.0));
    let box1 = ShapeHandle::new(Cuboid::new(Vector3::new(1.0, 1.0, 1.0)));
    
    // 添加物体到世界
    world.add(
        Isometry3::translation(0.0, 0.0, 0.0),
        ball,
        GeometricQueryType::Contacts(0.0),
        "球体",
    );
    
    world.add(
        Isometry3::translation(1.5, 0.0, 0.0),  // 会与球体碰撞的位置
        box1,
        GeometricQueryType::Contacts(0.0),
        "立方体",
    );
    
    // 更新世界状态
    world.update();
    
    // 检查碰撞
    println!("碰撞检测结果:");
    for (handle1, handle2) in world.contact_pairs() {
        println!("- {:?} 与 {:?} 发生碰撞", 
            world.collision_object(*handle1).data(),
            world.collision_object(*handle2).data()
        );
    }
}

这个完整示例演示了ncollide3d的主要功能:

  1. 基本形状间的碰撞检测
  2. 射线投射功能
  3. 复合形状的创建和使用
  4. 使用碰撞世界管理多个物体

要运行这个示例,请确保已正确配置Cargo.toml文件中的依赖项。

回到顶部