rapier物理库

This commit is contained in:
janing
2025-11-28 18:09:54 +08:00
parent 504423104b
commit 4e4863c7e6
5 changed files with 240 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
[build]
target = "wasm32-unknown-unknown"
[source.ustc]
registry = "sparse+https://mirrors.ustc.edu.cn/crates.io-index/"

20
pinball-physics/.gitignore vendored Normal file
View File

@@ -0,0 +1,20 @@
# Rust
/target/
**/*.rs.bk
*.pdb
Cargo.lock
# WASM
/pkg/
/wasm-pack.log
# IDE
.vscode/
.idea/
*.swp
*.swo
*~
# OS
.DS_Store
Thumbs.db

View File

@@ -0,0 +1,29 @@
[package]
name = "pinball-physics"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
rapier2d = "0.18"
# runtime utilities
once_cell = "1.21"
wasm-bindgen = { version = "0.2", optional = true }
# If you need wasm-bindgen for browser-side usage, enable the optional feature
[features]
default = []
wasm = ["wasm-bindgen"]
# Optional: useful for WASM builds (only pulled for wasm target)
[target.'cfg(target_arch = "wasm32")'.dependencies]
console_error_panic_hook = "0.1"
[profile.release]
opt-level = "z" # Optimize for size
lto = true # Enable Link Time Optimization
codegen-units = 1 # Reduce number of codegen units to increase optimizations

View File

@@ -0,0 +1,5 @@
[build]
target = "wasm32-unknown-unknown"
[source.ustc]
registry = "sparse+https://mirrors.ustc.edu.cn/crates.io-index/"

181
pinball-physics/src/lib.rs Normal file
View File

@@ -0,0 +1,181 @@
//! pinball-physics: 物理引擎库,同时支持原生 Rust 和 WASM 导出
use rapier2d::prelude::*;
use std::collections::HashMap;
/// 物理世界包装器 - 原生 Rust API
pub struct PhysicsWorld {
gravity: Vector<Real>,
integration_parameters: IntegrationParameters,
physics_pipeline: PhysicsPipeline,
island_manager: IslandManager,
broad_phase: BroadPhase,
narrow_phase: NarrowPhase,
impulse_joint_set: ImpulseJointSet,
multibody_joint_set: MultibodyJointSet,
ccd_solver: CCDSolver,
query_pipeline: QueryPipeline,
rigid_body_set: RigidBodySet,
collider_set: ColliderSet,
body_map: HashMap<u32, RigidBodyHandle>,
next_body_id: u32,
}
impl PhysicsWorld {
pub fn new(gravity_x: f32, gravity_y: f32) -> Self {
PhysicsWorld {
gravity: vector![gravity_x, gravity_y],
integration_parameters: IntegrationParameters::default(),
physics_pipeline: PhysicsPipeline::new(),
island_manager: IslandManager::new(),
broad_phase: BroadPhase::new(),
narrow_phase: NarrowPhase::new(),
impulse_joint_set: ImpulseJointSet::new(),
multibody_joint_set: MultibodyJointSet::new(),
ccd_solver: CCDSolver::new(),
query_pipeline: QueryPipeline::new(),
rigid_body_set: RigidBodySet::new(),
collider_set: ColliderSet::new(),
body_map: HashMap::new(),
next_body_id: 1,
}
}
pub fn step(&mut self) {
self.physics_pipeline.step(
&self.gravity,
&self.integration_parameters,
&mut self.island_manager,
&mut self.broad_phase,
&mut self.narrow_phase,
&mut self.rigid_body_set,
&mut self.collider_set,
&mut self.impulse_joint_set,
&mut self.multibody_joint_set,
&mut self.ccd_solver,
Some(&mut self.query_pipeline),
&(),
&(),
);
}
pub fn create_dynamic_body(&mut self, x: f32, y: f32) -> u32 {
let rb = RigidBodyBuilder::dynamic().translation(vector![x, y]).build();
let handle = self.rigid_body_set.insert(rb);
let id = self.next_body_id;
self.next_body_id = self.next_body_id.wrapping_add(1);
self.body_map.insert(id, handle);
id
}
pub fn create_static_body(&mut self, x: f32, y: f32) -> u32 {
let rb = RigidBodyBuilder::fixed().translation(vector![x, y]).build();
let handle = self.rigid_body_set.insert(rb);
let id = self.next_body_id;
self.next_body_id = self.next_body_id.wrapping_add(1);
self.body_map.insert(id, handle);
id
}
pub fn add_circle_collider(&mut self, body_id: u32, radius: f32) {
if let Some(&handle) = self.body_map.get(&body_id) {
let collider = ColliderBuilder::ball(radius).build();
self.collider_set.insert_with_parent(collider, handle, &mut self.rigid_body_set);
}
}
pub fn add_box_collider(&mut self, body_id: u32, half_width: f32, half_height: f32) {
if let Some(&handle) = self.body_map.get(&body_id) {
let collider = ColliderBuilder::cuboid(half_width, half_height).build();
self.collider_set.insert_with_parent(collider, handle, &mut self.rigid_body_set);
}
}
pub fn get_body_position(&self, body_id: u32) -> Option<(f32, f32)> {
if let Some(&handle) = self.body_map.get(&body_id) {
if let Some(body) = self.rigid_body_set.get(handle) {
let pos = body.translation();
return Some((pos.x, pos.y));
}
}
None
}
pub fn set_body_velocity(&mut self, body_id: u32, vx: f32, vy: f32) {
if let Some(&handle) = self.body_map.get(&body_id) {
if let Some(body) = self.rigid_body_set.get_mut(handle) {
body.set_linvel(vector![vx, vy], true);
}
}
}
pub fn apply_impulse(&mut self, body_id: u32, ix: f32, iy: f32) {
if let Some(&handle) = self.body_map.get(&body_id) {
if let Some(body) = self.rigid_body_set.get_mut(handle) {
body.apply_impulse(vector![ix, iy], true);
}
}
}
}
// WASM C ABI 导出(当编译为 wasm32 时)
#[cfg(target_arch = "wasm32")]
mod wasm_exports {
use super::*;
use std::sync::Mutex;
use once_cell::sync::Lazy;
type WorldId = u32;
static WORLDS: Lazy<Mutex<HashMap<WorldId, PhysicsWorld>>> = Lazy::new(|| Mutex::new(HashMap::new()));
static NEXT_WORLD_ID: Lazy<Mutex<WorldId>> = Lazy::new(|| Mutex::new(1));
#[no_mangle]
pub extern "C" fn pinball_create_world(gx: f32, gy: f32) -> WorldId {
let mut next = NEXT_WORLD_ID.lock().unwrap();
let id = *next;
*next = next.wrapping_add(1);
let world = PhysicsWorld::new(gx, gy);
WORLDS.lock().unwrap().insert(id, world);
id
}
#[no_mangle]
pub extern "C" fn pinball_step_world(world_id: WorldId) {
if let Some(world) = WORLDS.lock().unwrap().get_mut(&world_id) {
world.step();
}
}
#[no_mangle]
pub extern "C" fn pinball_create_dynamic_body(world_id: WorldId, x: f32, y: f32) -> u32 {
let mut map = WORLDS.lock().unwrap();
if let Some(world) = map.get_mut(&world_id) {
world.create_dynamic_body(x, y)
} else {
0
}
}
#[no_mangle]
pub extern "C" fn pinball_get_body_x(world_id: WorldId, body_id: u32) -> f32 {
let map = WORLDS.lock().unwrap();
if let Some(world) = map.get(&world_id) {
if let Some((x, _)) = world.get_body_position(body_id) {
return x;
}
}
0.0
}
#[no_mangle]
pub extern "C" fn pinball_get_body_y(world_id: WorldId, body_id: u32) -> f32 {
let map = WORLDS.lock().unwrap();
if let Some(world) = map.get(&world_id) {
if let Some((_, y)) = world.get_body_position(body_id) {
return y;
}
}
0.0
}
}