rapier物理库
This commit is contained in:
5
pinball-physics/.cargo/config.toml
Normal file
5
pinball-physics/.cargo/config.toml
Normal 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
20
pinball-physics/.gitignore
vendored
Normal 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
|
||||||
29
pinball-physics/Cargo.toml
Normal file
29
pinball-physics/Cargo.toml
Normal 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
|
||||||
5
pinball-physics/config.toml
Normal file
5
pinball-physics/config.toml
Normal 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
181
pinball-physics/src/lib.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user