RustPhysicsMQ/src/drone.rs

179 lines
5.7 KiB
Rust
Raw Normal View History

use nalgebra::{self as na, Vector3};
2025-12-08 21:18:04 +00:00
use rapier3d::prelude as rp;
use crate::engine::{ColliderExtraData, World};
2025-12-08 21:18:04 +00:00
pub mod controller;
pub mod fpvcontroller;
pub mod pidcontroller;
2025-12-08 21:18:04 +00:00
use controller::*;
const AREA_DENSITY: f32 = 1.23;
const DRAG_CONSTANT: f32 = 1.3;
const DRAG_MAGIC_NUM: f32 = 0.5;
2025-12-08 21:18:04 +00:00
pub struct MotorCharacteristics {
pub relative_motor_positions: [na::OPoint<f32, na::Const<3>>; 4],
pub max_thrust: f32,
pub max_torque: f32,
2025-12-08 21:18:04 +00:00
}
impl Default for MotorCharacteristics {
fn default() -> Self {
Self {
/*
* Motor position indices
* ^ - Front
* |
* |
* 1 --- 0
* | |
* | |
* 2 --- 3
*/
2025-12-08 21:18:04 +00:00
relative_motor_positions: [
rp::point![5.0, 3.0, 5.0],
rp::point![-5.0, 3.0, 5.0],
rp::point![-5.0, 3.0, -5.0],
rp::point![5.0, 3.0, -5.0],
],
max_thrust: 10.0,
max_torque: 1.0,
}
}
}
pub struct Drone {
pub rb_handle: rp::RigidBodyHandle,
2025-12-08 21:18:04 +00:00
motor_characteristics: MotorCharacteristics,
pub controller: Box<dyn DroneController>,
2025-12-09 21:47:05 +00:00
width: f32,
height: f32,
2025-12-08 21:18:04 +00:00
}
fn calculate_drag(velocity: f32, area: f32, constant: f32) -> f32 {
return 0.5 * AREA_DENSITY * (velocity * velocity) * area * constant * DRAG_MAGIC_NUM;
}
2025-12-08 21:18:04 +00:00
impl Drone {
pub fn new(
world: &mut World,
controller: Box<dyn DroneController>,
motor_characteristics: MotorCharacteristics,
mass: f32,
2025-12-08 21:18:04 +00:00
) -> Drone {
let drone_rb_handle = world.register_body(
rp::RigidBodyBuilder::dynamic()
.translation(rp::vector![0.0, 10.0, 0.0])
.rotation(rp::vector![0.0, 0.0, 0.0])
/*
* These damping values keep the simulation more realistic,
* They act as air resistance
*
* Values are kind of random for now. Calculating them requires the final model
* A Poor Man's fluid simulation :D
*/
2025-12-09 21:47:05 +00:00
// .linear_damping(0.2) // Damps velocity slowly
// .angular_damping(0.2) // Damps angular velocity slowly
2025-12-08 21:18:04 +00:00
.build(),
);
2025-12-09 21:47:05 +00:00
let width = 0.40;
let height = 0.25;
2025-12-08 21:18:04 +00:00
world.register_collider(
2025-12-09 21:47:05 +00:00
rp::ColliderBuilder::cuboid(width / 2.0, height / 2.0, width / 2.0)
.mass(mass)
2025-12-08 21:18:04 +00:00
.restitution(0.3)
.build(),
drone_rb_handle,
Some(ColliderExtraData {
2025-12-09 21:47:05 +00:00
color: macroquad::color::RED,
}),
2025-12-08 21:18:04 +00:00
);
// Should only need to be called once?
// No one should be changing motor characteristics during flight time.
2025-12-08 21:18:04 +00:00
controller.set_motor_characteristics(&motor_characteristics);
return Drone {
rb_handle: drone_rb_handle,
controller: controller,
motor_characteristics: motor_characteristics,
2025-12-09 21:47:05 +00:00
width,
height,
2025-12-08 21:18:04 +00:00
};
}
fn apply_drag(&mut self, world: &mut World) {
let body = world.bodies.get_mut(self.rb_handle).unwrap();
let velocity = body.linvel();
let mut drag = Vector3::<f32>::zeros();
let side_area = self.width * self.height;
let up_area = self.width * self.width;
drag.x = calculate_drag(velocity.x, side_area, DRAG_CONSTANT);
drag.y = calculate_drag(velocity.y, up_area, DRAG_CONSTANT);
drag.z = calculate_drag(velocity.z, side_area, DRAG_CONSTANT);
body.add_force(-drag, true);
}
fn apply_throttles(&mut self, world: &mut World) {
let throttles = self.controller.get_motor_throttles();
let drone_rb = world.bodies.get_mut(self.rb_handle).unwrap();
/*
* Clear all the previously applied forces and torques, or theyll add-up every tick
*/
drone_rb.reset_forces(true);
drone_rb.reset_torques(true);
for (i, motor_position) in self
.motor_characteristics
.relative_motor_positions
.iter()
.enumerate()
{
let throttle = throttles[i].clamp(0.0, 1.0);
let thrust = self.motor_characteristics.max_thrust * throttle;
let torque = self.motor_characteristics.max_torque * throttle;
// Thrust is applied upward at motor position.
//
// Force calculated with (0.0, thrust, 0.0) points to World Up,
// Apply RB's rotation to point at RB's Up
//
// motor_position is relative, transform it by the RB's position first
let mut thrust_force = nalgebra::Vector3::new(0.0, thrust, 0.0);
thrust_force = drone_rb.rotation().transform_vector(&thrust_force);
drone_rb.add_force_at_point(
thrust_force,
drone_rb.position().transform_point(motor_position),
true,
);
let torque = if i % 2 == 0 {
rp::vector![0.0, torque, 0.0]
} else {
rp::vector![0.0, -torque, 0.0]
};
drone_rb.add_torque(torque, true);
println!("Torque: \n{:?}", torque);
println!("Thrust: \n{:?}", thrust);
}
}
fn update_controller(&mut self, world: &World) {
2025-12-08 21:18:04 +00:00
let rb = world.bodies.get(self.rb_handle).unwrap();
self.controller.set_time(world.get_time());
self.controller.set_position(*rb.position());
}
pub fn process_tick(&mut self, world: &mut World) {
self.update_controller(world);
self.apply_throttles(world);
// self.apply_drag(world);
2025-12-08 21:18:04 +00:00
}
}