Add physics crate with rapier3d

This commit is contained in:
reo 2025-07-20 21:47:10 +03:00
parent a3d3f641cd
commit 8c9c310198
13 changed files with 756 additions and 102 deletions

View file

@ -5,9 +5,12 @@ use raidillon_ecs::Transform;
use raidillon_render::{Camera, ECSRenderer, init_render_window, DisplayHandle};
use raidillon_ui::Gui;
use raidillon_input::{Input, FPSCameraController};
use raidillon_physics::{Physics, BodyKind, RigidBodyComponent};
use rapier3d::prelude::RigidBodyHandle;
use winit::keyboard::KeyCode;
use winit::window::CursorGrabMode;
use winit::event::MouseButton;
use nalgebra::vector;
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
enum Action {
@ -22,7 +25,7 @@ fn main() -> Result<()> {
.build()
.expect("create event-loop");
let (window, _display): (winit::window::Window, DisplayHandle) = init_render_window(&event_loop, "raidillon", (1280, 720))?;
let (window, _display): (winit::window::Window, DisplayHandle) = init_render_window(&event_loop, "raidillon", (1920, 1080))?;
// Create ECS renderer which internally owns both the world and the renderer
let mut ecsr = ECSRenderer::from_display_handle(&_display)?;
@ -42,23 +45,55 @@ fn main() -> Result<()> {
let mut time = Time::new();
let object_ent = ecsr.load_mesh_from_gltf("resources/models/tree.gltf", Transform {
translation: Vec3::new(0.0, -2.5, -5.0),
// Physics world
let mut physics = Physics::new();
// Load a sphere model instead of the tree
let sphere_ent = ecsr.load_mesh_from_gltf("resources/models/uvsphere-smooth.gltf", Transform {
translation: Vec3::new(0.0, 2.5, 0.0),
rotation: Quat::IDENTITY,
scale: Vec3::new(0.01, 0.01, 0.01),
scale: Vec3::new(0.5, 0.5, 0.5),
})?;
{
let tr = *ecsr.world.get::<&Transform>(sphere_ent)?;
let collider = rapier3d::prelude::ColliderBuilder::ball(0.5).build();
let rb_handle = physics.add_rigid_body(BodyKind::Dynamic, tr, collider);
ecsr.world.insert_one(sphere_ent, RigidBodyComponent(rb_handle))?;
}
let ground_ent = ecsr.load_mesh_from_gltf("resources/models/plane.gltf", Transform {
translation: Vec3::new(0.0, -1.5, 0.0),
rotation: Quat::IDENTITY,
scale: Vec3::new(1.0, 1.0, 1.0),
scale: Vec3::new(10.0, 1.0, 10.0),
})?;
{
let tr = *ecsr.world.get::<&Transform>(ground_ent)?;
let collider = rapier3d::prelude::ColliderBuilder::cuboid(10.0, 0.1, 10.0).build();
let rb_handle = physics.add_rigid_body(BodyKind::Static, tr, collider);
ecsr.world.insert_one(ground_ent, RigidBodyComponent(rb_handle))?;
}
let player_initial_tr = Transform {
translation: Vec3::new(0.0, 1.0, 2.0),
rotation: Quat::IDENTITY,
scale: Vec3::ONE,
};
let player_collider = rapier3d::prelude::ColliderBuilder::capsule_y(0.9, 0.4).build();
let player_rb_handle: RigidBodyHandle = physics.add_rigid_body(BodyKind::Dynamic, player_initial_tr, player_collider);
if let Some(body) = physics.get_rigid_body_mut(player_rb_handle) {
body.set_locked_axes(rapier3d::prelude::LockedAxes::ROTATION_LOCKED, true);
}
let _player_ent = ecsr.world.spawn((player_initial_tr, RigidBodyComponent(player_rb_handle)));
camera_controller.position = player_initial_tr.translation;
let camera_ent = {
let (w, h): (u32, u32) = window.inner_size().into();
ecsr.world.spawn((Camera {
eye: Vec3::new(0.0, 0.0, 2.0),
eye: player_initial_tr.translation,
center: Vec3::ZERO,
up: Vec3::Y,
fovy: 60_f32.to_radians(),
@ -107,10 +142,17 @@ fn main() -> Result<()> {
}
WindowEvent::RedrawRequested => {
gui.render_world(&mut ecsr, &window, |ui, ecsr| {
if let Ok(mut tr) = ecsr.world.query_one_mut::<&mut Transform>(object_ent) {
if let Ok(mut tr) = ecsr.world.query_one_mut::<&mut Transform>(sphere_ent) {
ui.text("Hold right click to control the camera");
ui.text("WASD to move");
static mut SHOW_COLLIDERS: bool = true;
unsafe {
if ui.checkbox("Show Colliders", &mut SHOW_COLLIDERS) {
}
ecsr.renderer.set_show_colliders(SHOW_COLLIDERS);
}
// Translation controls
let mut translation = [tr.translation.x, tr.translation.y, tr.translation.z];
if ui.input_float3("Translation", &mut translation).build() {
@ -142,6 +184,7 @@ fn main() -> Result<()> {
{
let dt = time.delta_seconds();
camera_controller.update(
&input,
dt,
@ -149,15 +192,57 @@ fn main() -> Result<()> {
(Action::MoveForward, Action::MoveBackward, Action::MoveLeft, Action::MoveRight),
);
if let Ok(mut cam) = ecsr.world.query_one_mut::<&mut Camera>(camera_ent) {
cam.eye = camera_controller.position;
cam.center = camera_controller.position + camera_controller.front();
let mut move_dir = Vec3::ZERO;
let front = camera_controller.front();
let front_h = Vec3::new(front.x, 0.0, front.z).normalize_or_zero();
let right_vec = front_h.cross(Vec3::Y).normalize_or_zero();
if input.action_held(Action::MoveForward) { move_dir += front_h; }
if input.action_held(Action::MoveBackward) { move_dir -= front_h; }
if input.action_held(Action::MoveLeft) { move_dir -= right_vec; }
if input.action_held(Action::MoveRight) { move_dir += right_vec; }
if move_dir.length_squared() > 0.0 {
move_dir = move_dir.normalize();
}
if let Some(body) = physics.get_rigid_body_mut(player_rb_handle) {
let current_vel = body.linvel();
let desired_vel = move_dir * camera_controller.speed;
body.set_linvel(vector![desired_vel.x, current_vel.y, desired_vel.z], true);
}
physics.step(dt);
{
let mut query = ecsr.world.query::<(&mut Transform, &RigidBodyComponent)>();
for (_ent, (mut tr, rb_comp)) in query.iter() {
if let Some(body) = physics.get_rigid_body(rb_comp.0) {
let pos = body.position();
let translation = Physics::rapier_translation_to_glam(&pos.translation.vector);
let rotation = Physics::rapier_rotation_to_glam(&pos.rotation);
tr.translation = translation;
tr.rotation = rotation;
}
}
}
if let Some(player_body) = physics.get_rigid_body(player_rb_handle) {
let eye = Physics::rapier_translation_to_glam(&player_body.position().translation.vector);
camera_controller.position = eye;
if let Ok(mut cam) = ecsr.world.query_one_mut::<&mut Camera>(camera_ent) {
cam.eye = eye;
cam.center = eye + camera_controller.front();
}
}
}
input.end_frame();
gui.prepare_frame(&window);
ecsr.renderer.set_colliders(Some(&physics.collider_set));
window.request_redraw();
}
_ => {}