2025-12-10 13:11:51 +00:00
|
|
|
use nalgebra::{self as na, Vector3};
|
2025-12-08 21:18:04 +00:00
|
|
|
use rapier3d::prelude as rp;
|
|
|
|
|
|
2025-12-08 22:34:01 +00:00
|
|
|
use crate::engine::{ColliderExtraData, World};
|
2025-12-08 21:18:04 +00:00
|
|
|
|
2025-12-08 22:34:01 +00:00
|
|
|
pub mod controller;
|
2025-12-09 15:47:53 +00:00
|
|
|
pub mod fpvcontroller;
|
2025-12-09 19:23:57 +00:00
|
|
|
pub mod pidcontroller;
|
2025-12-08 21:18:04 +00:00
|
|
|
use controller::*;
|
|
|
|
|
|
2025-12-11 18:08:07 +00:00
|
|
|
const AIR_DENSITY: f32 = 1.23;
|
2025-12-10 13:11:51 +00:00
|
|
|
const DRAG_CONSTANT: f32 = 1.3;
|
|
|
|
|
const DRAG_MAGIC_NUM: f32 = 0.5;
|
|
|
|
|
|
2025-12-08 21:18:04 +00:00
|
|
|
pub struct MotorCharacteristics {
|
2025-12-08 22:34:01 +00:00
|
|
|
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 {
|
2025-12-08 22:34:01 +00:00
|
|
|
/*
|
|
|
|
|
* Motor position indices
|
2025-12-09 15:47:53 +00:00
|
|
|
* ^ - Front
|
|
|
|
|
* |
|
|
|
|
|
* |
|
2025-12-08 22:34:01 +00:00
|
|
|
* 1 --- 0
|
|
|
|
|
* | |
|
|
|
|
|
* | |
|
|
|
|
|
* 2 --- 3
|
|
|
|
|
*/
|
2025-12-08 21:18:04 +00:00
|
|
|
relative_motor_positions: [
|
2025-12-11 18:08:07 +00:00
|
|
|
rp::point![5.0, 0.0, 5.0],
|
|
|
|
|
rp::point![-5.0, 0.0, 5.0],
|
|
|
|
|
rp::point![-5.0, 0.0, -5.0],
|
|
|
|
|
rp::point![5.0, 0.0, -5.0],
|
2025-12-08 21:18:04 +00:00
|
|
|
],
|
|
|
|
|
max_thrust: 10.0,
|
|
|
|
|
max_torque: 1.0,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-08 22:34:01 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
2025-12-10 13:11:51 +00:00
|
|
|
fn calculate_drag(velocity: f32, area: f32, constant: f32) -> f32 {
|
2025-12-11 18:08:07 +00:00
|
|
|
return AIR_DENSITY * (velocity.abs() * velocity) * area * constant * DRAG_MAGIC_NUM;
|
2025-12-10 13:11:51 +00:00
|
|
|
}
|
|
|
|
|
|
2025-12-08 21:18:04 +00:00
|
|
|
impl Drone {
|
|
|
|
|
pub fn new(
|
|
|
|
|
world: &mut World,
|
|
|
|
|
controller: Box<dyn DroneController>,
|
|
|
|
|
motor_characteristics: MotorCharacteristics,
|
2025-12-08 22:34:01 +00:00
|
|
|
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])
|
2025-12-08 22:34:01 +00:00
|
|
|
.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)
|
2025-12-08 22:34:01 +00:00
|
|
|
.mass(mass)
|
2025-12-08 21:18:04 +00:00
|
|
|
.restitution(0.3)
|
|
|
|
|
.build(),
|
|
|
|
|
drone_rb_handle,
|
2025-12-08 22:34:01 +00:00
|
|
|
Some(ColliderExtraData {
|
2025-12-09 21:47:05 +00:00
|
|
|
color: macroquad::color::RED,
|
2025-12-08 22:34:01 +00:00
|
|
|
}),
|
2025-12-08 21:18:04 +00:00
|
|
|
);
|
|
|
|
|
|
2025-12-08 22:34:01 +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
|
|
|
};
|
|
|
|
|
}
|
2025-12-08 22:34:01 +00:00
|
|
|
|
2025-12-10 13:11:51 +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;
|
|
|
|
|
|
2025-12-11 18:08:07 +00:00
|
|
|
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);
|
|
|
|
|
// println!("Drag: {:}", drag);
|
2025-12-10 13:11:51 +00:00
|
|
|
|
2025-12-11 18:08:07 +00:00
|
|
|
body.add_force(drag, true);
|
2025-12-10 13:11:51 +00:00
|
|
|
}
|
|
|
|
|
|
2025-12-08 22:34:01 +00:00
|
|
|
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()
|
|
|
|
|
{
|
2025-12-11 18:08:07 +00:00
|
|
|
let throttle = throttles[i].clamp(-1.0, 1.0);
|
2025-12-08 22:34:01 +00:00
|
|
|
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]
|
|
|
|
|
};
|
2025-12-11 18:08:07 +00:00
|
|
|
drone_rb.add_torque(drone_rb.rotation().transform_vector(&torque), true);
|
2025-12-09 15:47:53 +00:00
|
|
|
println!("Torque: \n{:?}", torque);
|
|
|
|
|
println!("Thrust: \n{:?}", thrust);
|
2025-12-08 22:34:01 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn update_controller(&mut self, world: &World) {
|
2025-12-08 21:18:04 +00:00
|
|
|
let rb = world.bodies.get(self.rb_handle).unwrap();
|
2025-12-08 22:34:01 +00:00
|
|
|
self.controller.set_time(world.get_time());
|
2025-12-11 18:08:07 +00:00
|
|
|
// Pitch, Yaw, Roll
|
|
|
|
|
self.controller.set_angular_velocity(*rb.angvel());
|
|
|
|
|
self.controller.set_rotation(*rb.rotation());
|
2025-12-08 22:34:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn process_tick(&mut self, world: &mut World) {
|
|
|
|
|
self.update_controller(world);
|
|
|
|
|
self.apply_throttles(world);
|
2025-12-11 18:08:07 +00:00
|
|
|
self.apply_drag(world);
|
2025-12-08 21:18:04 +00:00
|
|
|
}
|
|
|
|
|
}
|