Skip to content

Commit 25973e6

Browse files
committed
feat: add Rapier 3D physics engine integration
Integrates rapier3d v0.32 behind an optional "physics" feature flag. Provides rigid body dynamics, colliders, joints, raycasting, collision events, and automatic scene node transform sync across all 7 platforms (macOS, iOS, tvOS, Windows, Linux, Android, Web/WASM).
1 parent 21ff900 commit 25973e6

17 files changed

Lines changed: 5020 additions & 23 deletions

File tree

native/android/src/lib.rs

Lines changed: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1125,3 +1125,299 @@ pub extern "C" fn bloom_commit_music(staging_handle: f64) -> f64 {
11251125
pub extern "C" fn bloom_run_game(_callback: extern "C" fn(f64)) {
11261126
// No-op on native. The TypeScript runGame() helper provides the while loop.
11271127
}
1128+
1129+
1130+
// ============================================================
1131+
// Physics (Rapier 3D)
1132+
// ============================================================
1133+
1134+
#[cfg(feature = "physics")]
1135+
use bloom_shared::physics::PhysicsWorld;
1136+
1137+
#[cfg(feature = "physics")]
1138+
fn physics() -> &'static mut PhysicsWorld {
1139+
engine().physics.as_mut().expect("Physics world not created. Call bloom_physics_create_world first.")
1140+
}
1141+
1142+
#[no_mangle]
1143+
pub extern "C" fn bloom_physics_create_world(gx: f64, gy: f64, gz: f64) {
1144+
#[cfg(feature = "physics")]
1145+
{ engine().physics = Some(PhysicsWorld::new(gx as f32, gy as f32, gz as f32)); }
1146+
}
1147+
#[no_mangle]
1148+
pub extern "C" fn bloom_physics_set_gravity(gx: f64, gy: f64, gz: f64) {
1149+
#[cfg(feature = "physics")]
1150+
if let Some(phys) = engine().physics.as_mut() { phys.set_gravity(gx as f32, gy as f32, gz as f32); }
1151+
}
1152+
#[no_mangle]
1153+
pub extern "C" fn bloom_physics_set_timestep(dt: f64, max_substeps: f64) {
1154+
#[cfg(feature = "physics")]
1155+
if let Some(phys) = engine().physics.as_mut() { phys.set_timestep(dt, max_substeps as u32); }
1156+
}
1157+
#[no_mangle]
1158+
pub extern "C" fn bloom_physics_create_body(body_type: f64, px: f64, py: f64, pz: f64, rx: f64, ry: f64, rz: f64, rw: f64) -> f64 {
1159+
#[cfg(feature = "physics")]
1160+
{ return physics().create_body(body_type, px, py, pz, rx, ry, rz, rw); }
1161+
#[cfg(not(feature = "physics"))] 0.0
1162+
}
1163+
#[no_mangle]
1164+
pub extern "C" fn bloom_physics_destroy_body(handle: f64) {
1165+
#[cfg(feature = "physics")]
1166+
if let Some(phys) = engine().physics.as_mut() { phys.destroy_body(handle); }
1167+
}
1168+
#[no_mangle]
1169+
pub extern "C" fn bloom_physics_set_body_enabled(handle: f64, enabled: f64) {
1170+
#[cfg(feature = "physics")]
1171+
if let Some(phys) = engine().physics.as_mut() { phys.set_body_enabled(handle, enabled != 0.0); }
1172+
}
1173+
#[no_mangle]
1174+
pub extern "C" fn bloom_physics_set_body_ccd(handle: f64, enabled: f64) {
1175+
#[cfg(feature = "physics")]
1176+
if let Some(phys) = engine().physics.as_mut() { phys.set_body_ccd(handle, enabled != 0.0); }
1177+
}
1178+
#[no_mangle]
1179+
pub extern "C" fn bloom_physics_set_body_gravity_scale(handle: f64, scale: f64) {
1180+
#[cfg(feature = "physics")]
1181+
if let Some(phys) = engine().physics.as_mut() { phys.set_body_gravity_scale(handle, scale as f32); }
1182+
}
1183+
#[no_mangle]
1184+
pub extern "C" fn bloom_physics_set_kinematic_target(handle: f64, px: f64, py: f64, pz: f64, rx: f64, ry: f64, rz: f64, rw: f64) {
1185+
#[cfg(feature = "physics")]
1186+
if let Some(phys) = engine().physics.as_mut() { phys.set_kinematic_target(handle, px, py, pz, rx, ry, rz, rw); }
1187+
}
1188+
#[no_mangle]
1189+
pub extern "C" fn bloom_physics_lock_rotations(handle: f64, lock_x: f64, lock_y: f64, lock_z: f64) {
1190+
#[cfg(feature = "physics")]
1191+
if let Some(phys) = engine().physics.as_mut() { phys.lock_rotations(handle, lock_x != 0.0, lock_y != 0.0, lock_z != 0.0); }
1192+
}
1193+
#[no_mangle]
1194+
pub extern "C" fn bloom_physics_add_box_collider(body: f64, hx: f64, hy: f64, hz: f64) -> f64 {
1195+
#[cfg(feature = "physics")]
1196+
{ return physics().add_box_collider(body, hx as f32, hy as f32, hz as f32); }
1197+
#[cfg(not(feature = "physics"))] 0.0
1198+
}
1199+
#[no_mangle]
1200+
pub extern "C" fn bloom_physics_add_sphere_collider(body: f64, radius: f64) -> f64 {
1201+
#[cfg(feature = "physics")]
1202+
{ return physics().add_sphere_collider(body, radius as f32); }
1203+
#[cfg(not(feature = "physics"))] 0.0
1204+
}
1205+
#[no_mangle]
1206+
pub extern "C" fn bloom_physics_add_capsule_collider(body: f64, half_height: f64, radius: f64) -> f64 {
1207+
#[cfg(feature = "physics")]
1208+
{ return physics().add_capsule_collider(body, half_height as f32, radius as f32); }
1209+
#[cfg(not(feature = "physics"))] 0.0
1210+
}
1211+
#[no_mangle]
1212+
pub extern "C" fn bloom_physics_add_cylinder_collider(body: f64, half_height: f64, radius: f64) -> f64 {
1213+
#[cfg(feature = "physics")]
1214+
{ return physics().add_cylinder_collider(body, half_height as f32, radius as f32); }
1215+
#[cfg(not(feature = "physics"))] 0.0
1216+
}
1217+
#[no_mangle]
1218+
pub extern "C" fn bloom_physics_set_collider_properties(collider: f64, friction: f64, restitution: f64, density: f64) {
1219+
#[cfg(feature = "physics")]
1220+
if let Some(phys) = engine().physics.as_mut() { phys.set_collider_properties(collider, friction as f32, restitution as f32, density as f32); }
1221+
}
1222+
#[no_mangle]
1223+
pub extern "C" fn bloom_physics_apply_force(body: f64, fx: f64, fy: f64, fz: f64) {
1224+
#[cfg(feature = "physics")]
1225+
if let Some(phys) = engine().physics.as_mut() { phys.apply_force(body, fx as f32, fy as f32, fz as f32); }
1226+
}
1227+
#[no_mangle]
1228+
pub extern "C" fn bloom_physics_apply_impulse(body: f64, ix: f64, iy: f64, iz: f64) {
1229+
#[cfg(feature = "physics")]
1230+
if let Some(phys) = engine().physics.as_mut() { phys.apply_impulse(body, ix as f32, iy as f32, iz as f32); }
1231+
}
1232+
#[no_mangle]
1233+
pub extern "C" fn bloom_physics_apply_torque(body: f64, tx: f64, ty: f64, tz: f64) {
1234+
#[cfg(feature = "physics")]
1235+
if let Some(phys) = engine().physics.as_mut() { phys.apply_torque(body, tx as f32, ty as f32, tz as f32); }
1236+
}
1237+
#[no_mangle]
1238+
pub extern "C" fn bloom_physics_apply_torque_impulse(body: f64, tx: f64, ty: f64, tz: f64) {
1239+
#[cfg(feature = "physics")]
1240+
if let Some(phys) = engine().physics.as_mut() { phys.apply_torque_impulse(body, tx as f32, ty as f32, tz as f32); }
1241+
}
1242+
#[no_mangle]
1243+
pub extern "C" fn bloom_physics_set_linear_velocity(body: f64, vx: f64, vy: f64, vz: f64) {
1244+
#[cfg(feature = "physics")]
1245+
if let Some(phys) = engine().physics.as_mut() { phys.set_linear_velocity(body, vx as f32, vy as f32, vz as f32); }
1246+
}
1247+
#[no_mangle]
1248+
pub extern "C" fn bloom_physics_set_angular_velocity(body: f64, vx: f64, vy: f64, vz: f64) {
1249+
#[cfg(feature = "physics")]
1250+
if let Some(phys) = engine().physics.as_mut() { phys.set_angular_velocity(body, vx as f32, vy as f32, vz as f32); }
1251+
}
1252+
#[no_mangle]
1253+
pub extern "C" fn bloom_physics_step(delta_time: f64) {
1254+
#[cfg(feature = "physics")]
1255+
{
1256+
let eng = engine();
1257+
let (physics, scene) = (&mut eng.physics, &mut eng.scene);
1258+
if let Some(phys) = physics.as_mut() { phys.step(delta_time); phys.sync_transforms(scene); }
1259+
}
1260+
}
1261+
#[no_mangle]
1262+
pub extern "C" fn bloom_physics_sync_transforms() {
1263+
#[cfg(feature = "physics")]
1264+
{
1265+
let eng = engine();
1266+
let (physics, scene) = (&mut eng.physics, &mut eng.scene);
1267+
if let Some(phys) = physics.as_mut() { phys.sync_transforms(scene); }
1268+
}
1269+
}
1270+
#[no_mangle]
1271+
pub extern "C" fn bloom_physics_get_body_position_x(body: f64) -> f64 {
1272+
#[cfg(feature = "physics")] { return physics().get_body_position_x(body); }
1273+
#[cfg(not(feature = "physics"))] 0.0
1274+
}
1275+
#[no_mangle]
1276+
pub extern "C" fn bloom_physics_get_body_position_y(body: f64) -> f64 {
1277+
#[cfg(feature = "physics")] { return physics().get_body_position_y(body); }
1278+
#[cfg(not(feature = "physics"))] 0.0
1279+
}
1280+
#[no_mangle]
1281+
pub extern "C" fn bloom_physics_get_body_position_z(body: f64) -> f64 {
1282+
#[cfg(feature = "physics")] { return physics().get_body_position_z(body); }
1283+
#[cfg(not(feature = "physics"))] 0.0
1284+
}
1285+
#[no_mangle]
1286+
pub extern "C" fn bloom_physics_get_body_rotation_x(body: f64) -> f64 {
1287+
#[cfg(feature = "physics")] { return physics().get_body_rotation_x(body); }
1288+
#[cfg(not(feature = "physics"))] 0.0
1289+
}
1290+
#[no_mangle]
1291+
pub extern "C" fn bloom_physics_get_body_rotation_y(body: f64) -> f64 {
1292+
#[cfg(feature = "physics")] { return physics().get_body_rotation_y(body); }
1293+
#[cfg(not(feature = "physics"))] 0.0
1294+
}
1295+
#[no_mangle]
1296+
pub extern "C" fn bloom_physics_get_body_rotation_z(body: f64) -> f64 {
1297+
#[cfg(feature = "physics")] { return physics().get_body_rotation_z(body); }
1298+
#[cfg(not(feature = "physics"))] 0.0
1299+
}
1300+
#[no_mangle]
1301+
pub extern "C" fn bloom_physics_get_body_rotation_w(body: f64) -> f64 {
1302+
#[cfg(feature = "physics")] { return physics().get_body_rotation_w(body); }
1303+
#[cfg(not(feature = "physics"))] 1.0
1304+
}
1305+
#[no_mangle]
1306+
pub extern "C" fn bloom_physics_get_linear_velocity_x(body: f64) -> f64 {
1307+
#[cfg(feature = "physics")] { return physics().get_linear_velocity_x(body); }
1308+
#[cfg(not(feature = "physics"))] 0.0
1309+
}
1310+
#[no_mangle]
1311+
pub extern "C" fn bloom_physics_get_linear_velocity_y(body: f64) -> f64 {
1312+
#[cfg(feature = "physics")] { return physics().get_linear_velocity_y(body); }
1313+
#[cfg(not(feature = "physics"))] 0.0
1314+
}
1315+
#[no_mangle]
1316+
pub extern "C" fn bloom_physics_get_linear_velocity_z(body: f64) -> f64 {
1317+
#[cfg(feature = "physics")] { return physics().get_linear_velocity_z(body); }
1318+
#[cfg(not(feature = "physics"))] 0.0
1319+
}
1320+
#[no_mangle]
1321+
pub extern "C" fn bloom_physics_get_angular_velocity_x(body: f64) -> f64 {
1322+
#[cfg(feature = "physics")] { return physics().get_angular_velocity_x(body); }
1323+
#[cfg(not(feature = "physics"))] 0.0
1324+
}
1325+
#[no_mangle]
1326+
pub extern "C" fn bloom_physics_get_angular_velocity_y(body: f64) -> f64 {
1327+
#[cfg(feature = "physics")] { return physics().get_angular_velocity_y(body); }
1328+
#[cfg(not(feature = "physics"))] 0.0
1329+
}
1330+
#[no_mangle]
1331+
pub extern "C" fn bloom_physics_get_angular_velocity_z(body: f64) -> f64 {
1332+
#[cfg(feature = "physics")] { return physics().get_angular_velocity_z(body); }
1333+
#[cfg(not(feature = "physics"))] 0.0
1334+
}
1335+
#[no_mangle]
1336+
pub extern "C" fn bloom_physics_raycast(ox: f64, oy: f64, oz: f64, dx: f64, dy: f64, dz: f64, max_dist: f64) -> f64 {
1337+
#[cfg(feature = "physics")]
1338+
{ if physics().raycast(ox, oy, oz, dx, dy, dz, max_dist) { return 1.0; } else { return 0.0; } }
1339+
#[cfg(not(feature = "physics"))] 0.0
1340+
}
1341+
#[no_mangle]
1342+
pub extern "C" fn bloom_physics_ray_hit_body() -> f64 {
1343+
#[cfg(feature = "physics")] { return physics().last_ray_hit.as_ref().map_or(0.0, |h| h.body_handle); }
1344+
#[cfg(not(feature = "physics"))] 0.0
1345+
}
1346+
#[no_mangle]
1347+
pub extern "C" fn bloom_physics_ray_hit_distance() -> f64 {
1348+
#[cfg(feature = "physics")] { return physics().last_ray_hit.as_ref().map_or(0.0, |h| h.distance); }
1349+
#[cfg(not(feature = "physics"))] 0.0
1350+
}
1351+
#[no_mangle]
1352+
pub extern "C" fn bloom_physics_ray_hit_x() -> f64 {
1353+
#[cfg(feature = "physics")] { return physics().last_ray_hit.as_ref().map_or(0.0, |h| h.point[0]); }
1354+
#[cfg(not(feature = "physics"))] 0.0
1355+
}
1356+
#[no_mangle]
1357+
pub extern "C" fn bloom_physics_ray_hit_y() -> f64 {
1358+
#[cfg(feature = "physics")] { return physics().last_ray_hit.as_ref().map_or(0.0, |h| h.point[1]); }
1359+
#[cfg(not(feature = "physics"))] 0.0
1360+
}
1361+
#[no_mangle]
1362+
pub extern "C" fn bloom_physics_ray_hit_z() -> f64 {
1363+
#[cfg(feature = "physics")] { return physics().last_ray_hit.as_ref().map_or(0.0, |h| h.point[2]); }
1364+
#[cfg(not(feature = "physics"))] 0.0
1365+
}
1366+
#[no_mangle]
1367+
pub extern "C" fn bloom_physics_get_collision_count() -> f64 {
1368+
#[cfg(feature = "physics")] { return physics().collision_events.len() as f64; }
1369+
#[cfg(not(feature = "physics"))] 0.0
1370+
}
1371+
#[no_mangle]
1372+
pub extern "C" fn bloom_physics_get_collision_event(index: f64) -> f64 {
1373+
#[cfg(feature = "physics")]
1374+
{
1375+
let phys = physics();
1376+
let i = index as usize;
1377+
if i < phys.collision_events.len() {
1378+
let evt = &phys.collision_events[i];
1379+
phys.last_collision_read = (evt.body_a, evt.body_b, evt.started);
1380+
return evt.body_a;
1381+
}
1382+
return 0.0;
1383+
}
1384+
#[cfg(not(feature = "physics"))] 0.0
1385+
}
1386+
#[no_mangle]
1387+
pub extern "C" fn bloom_physics_get_collision_body_b() -> f64 {
1388+
#[cfg(feature = "physics")] { return physics().last_collision_read.1; }
1389+
#[cfg(not(feature = "physics"))] 0.0
1390+
}
1391+
#[no_mangle]
1392+
pub extern "C" fn bloom_physics_get_collision_started() -> f64 {
1393+
#[cfg(feature = "physics")] { return if physics().last_collision_read.2 { 1.0 } else { 0.0 }; }
1394+
#[cfg(not(feature = "physics"))] 0.0
1395+
}
1396+
#[no_mangle]
1397+
pub extern "C" fn bloom_physics_attach_scene_node(body: f64, scene_node: f64) {
1398+
#[cfg(feature = "physics")]
1399+
if let Some(phys) = engine().physics.as_mut() { phys.attach_scene_node(body, scene_node); }
1400+
}
1401+
#[no_mangle]
1402+
pub extern "C" fn bloom_physics_create_fixed_joint(body_a: f64, body_b: f64, ax: f64, ay: f64, az: f64, bx: f64, by: f64, bz: f64) -> f64 {
1403+
#[cfg(feature = "physics")]
1404+
{ return physics().create_fixed_joint(body_a, body_b, ax as f32, ay as f32, az as f32, bx as f32, by as f32, bz as f32); }
1405+
#[cfg(not(feature = "physics"))] 0.0
1406+
}
1407+
#[no_mangle]
1408+
pub extern "C" fn bloom_physics_create_revolute_joint(body_a: f64, body_b: f64, ax: f64, ay: f64, az: f64, axis_x: f64, axis_y: f64, axis_z: f64) -> f64 {
1409+
#[cfg(feature = "physics")]
1410+
{ return physics().create_revolute_joint(body_a, body_b, ax as f32, ay as f32, az as f32, axis_x as f32, axis_y as f32, axis_z as f32); }
1411+
#[cfg(not(feature = "physics"))] 0.0
1412+
}
1413+
#[no_mangle]
1414+
pub extern "C" fn bloom_physics_create_prismatic_joint(body_a: f64, body_b: f64, ax: f64, ay: f64, az: f64, axis_x: f64, axis_y: f64, axis_z: f64) -> f64 {
1415+
#[cfg(feature = "physics")]
1416+
{ return physics().create_prismatic_joint(body_a, body_b, ax as f32, ay as f32, az as f32, axis_x as f32, axis_y as f32, axis_z as f32); }
1417+
#[cfg(not(feature = "physics"))] 0.0
1418+
}
1419+
#[no_mangle]
1420+
pub extern "C" fn bloom_physics_destroy_joint(handle: f64) {
1421+
#[cfg(feature = "physics")]
1422+
if let Some(phys) = engine().physics.as_mut() { phys.destroy_joint(handle); }
1423+
}

0 commit comments

Comments
 (0)