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);
}
}
性能提示
- 对于静态几何体,使用
CollisionObject::set_prediction
设置较大的预测距离可以减少更新频率 - 对于大量物体,考虑使用
BroadPhase
进行粗检测优化 - 重复使用的形状应该使用
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的主要功能:
- 基本形状间的碰撞检测
- 射线投射功能
- 复合形状的创建和使用
- 使用碰撞世界管理多个物体
要运行这个示例,请确保已正确配置Cargo.toml文件中的依赖项。