296 lines
8 KiB
Rust
296 lines
8 KiB
Rust
use std::fmt::Debug;
|
|
|
|
use bevy::prelude::{shape::UVSphere 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;
|
|
|
|
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: 0.65,
|
|
rear_back: 1.1,
|
|
y: -1.5,
|
|
limits: [-1.0, 0.0],
|
|
stiffness: 80.0,
|
|
damping: 0.6,
|
|
radius: 0.4,
|
|
}
|
|
}
|
|
}
|
|
|
|
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 altitude = PLANET_RADIUS - 220.0;
|
|
|
|
let mut xform = Transform::from_translation(Vec3::X * altitude)
|
|
.with_rotation(Quat::from_axis_angle(Vec3::Z, -89.0f32.to_radians()));
|
|
//.with_rotation(Quat::from_axis_angle(Vec3::X, 140.0f32.to_radians()));
|
|
|
|
let right = xform.right() * 350.0;
|
|
xform.translation += right;
|
|
|
|
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();
|
|
|
|
spawn_tires(&mut commands, &xform, bike, &wheel_conf, &mut meshterials);
|
|
}
|
|
|
|
fn re_tire(
|
|
mut commands: Commands,
|
|
wheel_conf: ResMut<WheelConfig>,
|
|
mut meshterials: Meshterial,
|
|
bquery: Query<(Entity, &Transform), With<CyberBikeBody>>,
|
|
wheels: Query<Entity, With<CyberWheel>>,
|
|
) {
|
|
// we fuck with values in the egui inspector
|
|
let (bike, xform) = bquery.single();
|
|
if wheel_conf.is_changed() {
|
|
for wheel in wheels.iter() {
|
|
commands.entity(wheel).despawn_recursive();
|
|
}
|
|
spawn_tires(&mut commands, xform, bike, &wheel_conf, &mut meshterials);
|
|
}
|
|
}
|
|
|
|
fn spawn_tires(
|
|
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,
|
|
..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 = 0.0;
|
|
let wheel_z = conf.rear_back;
|
|
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 = conf.damping;
|
|
let prismatic = PrismaticJointBuilder::new(Vec3::Y)
|
|
.local_anchor1(offset)
|
|
.limits(limits)
|
|
.motor_position(-1.0, stiffness, damping);
|
|
let joint = ImpulseJoint::new(bike, prismatic);
|
|
|
|
let spatial_bundle = SpatialBundle {
|
|
transform: wheel_pos_in_world,
|
|
..Default::default()
|
|
};
|
|
|
|
let tire_spundle = SpatialBundle {
|
|
transform: Transform::IDENTITY,
|
|
..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(
|
|
TransformInterpolation {
|
|
start: None,
|
|
end: None,
|
|
},
|
|
);
|
|
})
|
|
.insert(TransformInterpolation {
|
|
start: None,
|
|
end: None,
|
|
})
|
|
.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)
|
|
.add_system(re_tire);
|
|
}
|
|
}
|