From 4e4863c7e61a149e837dc8033aace8034bbb32ba Mon Sep 17 00:00:00 2001 From: janing <1175861874@qq.com> Date: Fri, 28 Nov 2025 18:09:54 +0800 Subject: [PATCH] =?UTF-8?q?rapier=E7=89=A9=E7=90=86=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pinball-physics/.cargo/config.toml | 5 + pinball-physics/.gitignore | 20 ++++ pinball-physics/Cargo.toml | 29 +++++ pinball-physics/config.toml | 5 + pinball-physics/src/lib.rs | 181 +++++++++++++++++++++++++++++ 5 files changed, 240 insertions(+) create mode 100644 pinball-physics/.cargo/config.toml create mode 100644 pinball-physics/.gitignore create mode 100644 pinball-physics/Cargo.toml create mode 100644 pinball-physics/config.toml create mode 100644 pinball-physics/src/lib.rs diff --git a/pinball-physics/.cargo/config.toml b/pinball-physics/.cargo/config.toml new file mode 100644 index 0000000..a5b4964 --- /dev/null +++ b/pinball-physics/.cargo/config.toml @@ -0,0 +1,5 @@ +[build] +target = "wasm32-unknown-unknown" + +[source.ustc] +registry = "sparse+https://mirrors.ustc.edu.cn/crates.io-index/" \ No newline at end of file diff --git a/pinball-physics/.gitignore b/pinball-physics/.gitignore new file mode 100644 index 0000000..f6d39e5 --- /dev/null +++ b/pinball-physics/.gitignore @@ -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 diff --git a/pinball-physics/Cargo.toml b/pinball-physics/Cargo.toml new file mode 100644 index 0000000..e2dd50c --- /dev/null +++ b/pinball-physics/Cargo.toml @@ -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 diff --git a/pinball-physics/config.toml b/pinball-physics/config.toml new file mode 100644 index 0000000..a5b4964 --- /dev/null +++ b/pinball-physics/config.toml @@ -0,0 +1,5 @@ +[build] +target = "wasm32-unknown-unknown" + +[source.ustc] +registry = "sparse+https://mirrors.ustc.edu.cn/crates.io-index/" \ No newline at end of file diff --git a/pinball-physics/src/lib.rs b/pinball-physics/src/lib.rs new file mode 100644 index 0000000..ad697f6 --- /dev/null +++ b/pinball-physics/src/lib.rs @@ -0,0 +1,181 @@ +//! pinball-physics: 物理引擎库,同时支持原生 Rust 和 WASM 导出 + +use rapier2d::prelude::*; +use std::collections::HashMap; + +/// 物理世界包装器 - 原生 Rust API +pub struct PhysicsWorld { + gravity: Vector, + 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, + 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>> = Lazy::new(|| Mutex::new(HashMap::new())); + static NEXT_WORLD_ID: Lazy> = 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 + } +}