cyber_rider/src/bike.rs
2023-01-24 23:04:33 -08:00

272 lines
7.2 KiB
Rust

use std::fmt::Debug;
use bevy::prelude::{shape::Capsule as Tire, *};
use bevy_rapier3d::{
geometry::Group,
prelude::{
Ccd, Collider, ColliderMassProperties, CollisionGroups, Damping, ExternalForce, Friction,
ImpulseJoint, PrismaticJointBuilder, Restitution, RigidBody, Sleeping,
TransformInterpolation, Velocity,
},
};
use crate::planet::PLANET_RADIUS;
pub(crate) const SPAWN_ALTITUDE: f32 = PLANET_RADIUS * 0.2;
type Meshterial<'a> = (
ResMut<'a, Assets<Mesh>>,
ResMut<'a, Assets<StandardMaterial>>,
);
#[derive(Component)]
pub struct CyberBikeBody;
#[derive(Component)]
pub struct CyberBikeCollider;
#[derive(Component, Debug)]
pub struct CyberBikeModel;
#[derive(Debug, Component)]
pub struct CyberWheel;
#[derive(Component, Debug, Default, Clone, Copy)]
pub struct CyberBikeControl {
pub error_sum: f32,
pub prev_error: f32,
}
#[derive(Resource, Reflect)]
#[reflect(Resource)]
pub struct WheelConfig {
pub front_forward: f32,
pub front_stance: f32,
pub rear_back: f32,
pub y: f32,
pub limits: [f32; 2],
pub stiffness: f32,
pub damping: f32,
pub radius: f32,
}
impl Default for WheelConfig {
fn default() -> Self {
Self {
front_forward: 0.9,
front_stance: 1.2,
rear_back: 1.1,
y: -1.0,
limits: [0.1, 1.0],
stiffness: 10.0,
damping: 0.7,
radius: 0.3,
}
}
}
const BIKE_BODY_COLLISION_GROUP: (Group, Group) = (Group::GROUP_1, Group::GROUP_1);
const BIKE_WHEEL_COLLISION_GROUP: (Group, Group) = (Group::GROUP_10, Group::GROUP_10);
fn spawn_cyberbike(
mut commands: Commands,
asset_server: Res<AssetServer>,
wheel_conf: Res<WheelConfig>,
mut meshterials: Meshterial,
) {
let xform = Transform::from_translation(Vec3::Y * SPAWN_ALTITUDE);
//.with_rotation(Quat::from_axis_angle(Vec3::Z, -89.0f32.to_radians()));
let damping = Damping {
angular_damping: 0.5,
linear_damping: 0.1,
};
let not_sleeping = Sleeping::disabled();
let ccd = Ccd { enabled: true };
let bcollider_shape =
Collider::capsule(Vec3::new(0.0, 0.0, -1.0), Vec3::new(0.0, 0.0, 1.0), 0.50);
let friction = Friction {
coefficient: 0.0,
..Default::default()
};
let restitution = Restitution {
coefficient: 0.0,
..Default::default()
};
let mass_properties = ColliderMassProperties::Density(0.2);
let (membership, filter) = BIKE_BODY_COLLISION_GROUP;
let bike_collision_group = CollisionGroups::new(membership, filter);
let scene = asset_server.load("cb-no-y_up.glb#Scene0");
let spatialbundle = SpatialBundle {
transform: xform,
..Default::default()
};
let bike = commands
.spawn(RigidBody::Dynamic)
.insert(spatialbundle)
.insert((
bcollider_shape,
bike_collision_group,
mass_properties,
damping,
restitution,
friction,
not_sleeping,
ccd,
))
.insert(TransformInterpolation {
start: None,
end: None,
})
.insert(Velocity::zero())
.insert(ExternalForce::default())
.insert(CyberBikeCollider)
.with_children(|rider| {
rider.spawn(SceneBundle {
scene,
..Default::default()
});
})
.insert(CyberBikeModel)
.insert(CyberBikeBody)
.insert(CyberBikeControl::default())
.id();
//return;
re_tire(&mut commands, &xform, bike, &wheel_conf, &mut meshterials);
}
fn re_tire(
commands: &mut Commands,
xform: &Transform,
bike: Entity,
conf: &WheelConfig,
meshterials: &mut Meshterial,
) {
// re-set the collision group
let (membership, filter) = BIKE_WHEEL_COLLISION_GROUP;
let wheels_collision_group = CollisionGroups::new(membership, filter);
let wheel_y = conf.y;
let wheel_rad = conf.radius;
let stiffness = conf.stiffness;
let not_sleeping = Sleeping::disabled();
let ccd = Ccd { enabled: true };
let limits = conf.limits;
let (meshes, materials) = meshterials;
let tire = Tire {
radius: wheel_rad,
rings: 1,
depth: 0.2,
..Default::default()
};
let material = StandardMaterial {
base_color: Color::Rgba {
red: 0.01,
green: 0.01,
blue: 0.01,
alpha: 1.0,
},
alpha_mode: AlphaMode::Opaque,
perceptual_roughness: 0.5,
..Default::default()
};
let pbr_bundle = PbrBundle {
material: materials.add(material),
mesh: meshes.add(Mesh::from(tire)),
..Default::default()
};
let mut wheel_poses = Vec::with_capacity(3);
// left front
{
let wheel_x = -conf.front_stance;
let wheel_z = conf.front_forward;
let offset = Vec3::new(wheel_x, wheel_y, wheel_z);
wheel_poses.push(offset);
}
// right front
{
let wheel_x = conf.front_stance;
let wheel_z = conf.front_forward;
let offset = Vec3::new(wheel_x, wheel_y, wheel_z);
wheel_poses.push(offset);
}
// rear
{
let wheel_x = -conf.front_stance;
let wheel_z = conf.front_forward;
let offset = Vec3::new(wheel_x, wheel_y, wheel_z);
wheel_poses.push(offset);
}
for offset in wheel_poses {
let trans = xform.translation + offset;
let wheel_pos_in_world = Transform::from_rotation(xform.rotation).with_translation(trans);
let wheel_damping = Damping {
linear_damping: 0.8,
..Default::default()
};
let wheel_collider = Collider::ball(wheel_rad);
let mass_props = ColliderMassProperties::Density(0.001);
let damping = 0.3;
let prismatic = PrismaticJointBuilder::new(Vec3::Y)
.local_anchor2(offset)
.limits(limits)
.motor_position(0.0, stiffness, damping);
let joint = ImpulseJoint::new(bike, prismatic);
let spatial_bundle = SpatialBundle {
transform: wheel_pos_in_world,
..Default::default()
};
let txform = wheel_pos_in_world.with_rotation(Quat::from_axis_angle(
wheel_pos_in_world.forward(),
90.0f32.to_radians(),
));
let tire_spundle = SpatialBundle {
transform: txform,
..Default::default()
};
commands
.spawn(RigidBody::Dynamic)
.insert(spatial_bundle)
.insert((
wheel_collider,
mass_props,
wheel_damping,
ccd,
not_sleeping,
joint,
wheels_collision_group,
))
.with_children(|wheel| {
wheel.spawn(tire_spundle).insert(pbr_bundle.clone());
})
.insert(CyberWheel);
}
}
pub struct CyberBikePlugin;
impl Plugin for CyberBikePlugin {
fn build(&self, app: &mut App) {
app.insert_resource(WheelConfig::default())
.register_type::<WheelConfig>()
.add_startup_system_to_stage(StartupStage::PostStartup, spawn_cyberbike);
}
}