checkpoint, still not compiling

This commit is contained in:
Joe Ardent 2024-07-14 22:52:24 -07:00
parent 3f965652d1
commit 6b9a879927
9 changed files with 151 additions and 255 deletions

View file

@ -1,6 +1,6 @@
use avian3d::prelude::*;
use bevy::{
app::Startup,
app::{Startup, Update},
diagnostic::FrameTimeDiagnosticsPlugin,
ecs::reflect::ReflectResource,
prelude::{App, IntoSystemConfigs, Plugin, Resource},
@ -29,20 +29,14 @@ impl Plugin for CyberActionPlugin {
.init_resource::<CyberLean>()
.register_type::<CyberLean>()
.register_type::<CatControllerSettings>()
.add_plugins(PhysicsPlugins::default())
.add_systems(Startup, timestep_setup)
.add_plugins(FrameTimeDiagnosticsPlugin::default())
.add_plugins((
PhysicsPlugins::default(),
FrameTimeDiagnosticsPlugin::default(),
))
.insert_resource(SubstepCount(12))
.add_systems(
(
gravity,
cyber_lean,
falling_cat,
input_forces,
drag,
tunnel_out,
surface_fix,
)
.chain(),
Update,
(gravity, cyber_lean, falling_cat, input_forces).chain(),
);
}
}

View file

@ -1,6 +1,8 @@
use std::f32::consts::{FRAC_PI_3, FRAC_PI_4};
use avian3d::prelude::*;
use avian3d::{
dynamics::solver::xpbd::AngularConstraint, parry::mass_properties::MassProperties, prelude::*,
};
use bevy::prelude::{
Commands, Entity, Quat, Query, Res, ResMut, Time, Transform, Vec3, With, Without,
};
@ -33,46 +35,28 @@ fn rotate_point(pt: &Vec3, rot: &Quat) -> Vec3 {
-Vec3::from_array([rot_qpt.x, rot_qpt.y, rot_qpt.z])
}
pub(super) fn timestep_setup(mut config: ResMut<RapierConfiguration>) {
let ts = TimestepMode::Fixed {
dt: 1.0 / 60.0,
substeps: 2,
};
config.timestep_mode = ts;
}
/// The gravity vector points from the cyberbike to the center of the planet.
pub(super) fn gravity(
mut query: Query<(&Transform, &mut ExternalForce), With<CyberBikeBody>>,
settings: Res<MovementSettings>,
mut rapier_config: ResMut<RapierConfiguration>,
mut gravity: ResMut<Gravity>,
#[cfg(feature = "inspector")] mut debug_instant: ResMut<ActionDebugInstant>,
) {
let (xform, mut forces) = query.single_mut();
#[cfg(feature = "inspectorb")]
{
if debug_instant.elapsed().as_millis() > 6000 {
dbg!(&forces);
debug_instant.reset();
}
}
rapier_config.gravity = xform.translation.normalize() * -settings.gravity;
forces.force = Vec3::ZERO;
forces.torque = Vec3::ZERO;
*gravity = Gravity(xform.translation.normalize() * -settings.gravity);
forces.clear();
}
/// The desired lean angle, given steering input and speed.
pub(super) fn cyber_lean(
bike_state: Query<(&Velocity, &Transform), With<CyberBikeBody>>,
bike_state: Query<(&LinearVelocity, &Transform), With<CyberBikeBody>>,
wheels: Query<&Transform, With<CyberWheel>>,
input: Res<InputState>,
gravity_settings: Res<MovementSettings>,
mut lean: ResMut<CyberLean>,
) {
let (velocity, xform) = bike_state.single();
let vel = velocity.linvel.dot(xform.forward());
let vel = velocity.dot(*xform.forward());
let v_squared = vel.powi(2);
let steering_angle = yaw_to_angle(input.yaw);
let wheels: Vec<_> = wheels.iter().map(|w| w.translation).collect();
@ -91,20 +75,20 @@ pub(super) fn cyber_lean(
/// PID-based controller for stabilizing attitude; keeps the cyberbike upright.
pub(super) fn falling_cat(
mut bike_query: Query<(&Transform, &mut ExternalForce, &mut CatControllerState)>,
mut bike_query: Query<(&Transform, &mut ExternalTorque, &mut CatControllerState)>,
time: Res<Time>,
settings: Res<CatControllerSettings>,
lean: Res<CyberLean>,
) {
let (xform, mut forces, mut control_vars) = bike_query.single_mut();
let (xform, mut torque, mut control_vars) = bike_query.single_mut();
let world_up = xform.translation.normalize();
let rot = Quat::from_axis_angle(xform.back(), lean.lean);
let rot = Quat::from_axis_angle(*xform.back(), lean.lean);
let target_up = rotate_point(&world_up, &rot).normalize();
let bike_right = xform.right();
let roll_error = bike_right.dot(target_up);
let pitch_error = world_up.dot(xform.back());
let pitch_error = world_up.dot(*xform.back());
// only try to correct roll if we're not totally vertical
if pitch_error.abs() < 0.95 {
@ -112,7 +96,7 @@ pub(super) fn falling_cat(
let mag =
(settings.kp * roll_error) + (settings.ki * integral) + (settings.kd * derivative);
if mag.is_finite() {
forces.torque += xform.back() * mag;
torque.apply_torque(*xform.back() * mag);
}
}
}
@ -121,20 +105,27 @@ pub(super) fn falling_cat(
pub(super) fn input_forces(
settings: Res<MovementSettings>,
input: Res<InputState>,
mut braking_query: Query<&mut MultibodyJoint, (Without<CyberSteering>, With<CyberWheel>)>,
time: Res<Time>,
axle: Query<Entity, With<CyberSteering>>,
mut braking_query: Query<&mut ExternalTorque, With<CyberWheel>>,
mut body_query: Query<
(&Transform, &mut ExternalForce),
(With<CyberBikeBody>, Without<CyberSteering>),
(
Entity,
&Transform,
&ColliderMassProperties,
&mut ExternalForce,
),
With<CyberBikeBody>,
>,
mut steering_query: Query<&mut MultibodyJoint, With<CyberSteering>>,
) {
let (xform, mut forces) = body_query.single_mut();
let (bike, xform, mass, mut forces) = body_query.single_mut();
let dt = time.delta_seconds();
// thrust
let thrust = xform.forward() * input.throttle * settings.accel;
let point = xform.translation + xform.back();
let force = ExternalForce::at_point(thrust, point, xform.translation);
*forces += force;
let point = xform.translation + *xform.back();
*forces.apply_force_at_point(dt * thrust, point, mass.center_of_mass.0);
// brake + thrust
for mut motor in braking_query.iter_mut() {
@ -144,99 +135,14 @@ pub(super) fn input_forces(
input.throttle * settings.accel
};
let speed = if input.brake { 0.0 } else { -70.0 };
motor.data = (*motor
.data
.as_revolute_mut()
.unwrap()
.set_motor_max_force(factor)
.set_motor_velocity(speed, factor))
.into();
let target = dt * factor * speed;
let torque = target * Vec3::X;
motor.apply_torque(torque);
}
// steering
let angle = yaw_to_angle(input.yaw);
let mut steering = steering_query.single_mut();
steering.data = (*steering
.data
.as_revolute_mut()
.unwrap()
.set_motor_position(-angle, 100.0, 0.5))
.into();
}
/// Don't let the wheels get stuck underneat the planet
pub(super) fn surface_fix(
mut commands: Commands,
mut wheel_query: Query<
(Entity, &Transform, &mut CollisionGroups),
(With<CyberWheel>, Without<Tunneling>),
>,
span_query: Query<&Transform, With<CyberWheel>>,
config: Res<WheelConfig>,
context: Res<RapierContext>,
) {
let mut wheels = Vec::new();
for xform in span_query.iter() {
wheels.push(xform);
}
let span = (wheels[1].translation - wheels[0].translation).normalize();
for (entity, xform, mut cgroups) in wheel_query.iter_mut() {
//let ray_dir = xform.translation.normalize();
let ray_dir = xform.right().cross(span).normalize();
if let Some(hit) = context.cast_ray_and_get_normal(
xform.translation,
ray_dir,
config.radius * 1.1,
false,
QueryFilter::only_fixed(),
) {
cgroups.memberships = Group::NONE;
cgroups.filters = Group::NONE;
commands.entity(entity).insert(Tunneling {
frames: 3,
dir: hit.1.normal,
});
}
}
}
pub(super) fn tunnel_out(
mut commands: Commands,
mut wheel_query: Query<
(
Entity,
&mut Tunneling,
&mut ExternalForce,
&mut CollisionGroups,
),
With<CyberWheel>,
>,
mprops: Query<&ReadMassProperties, With<CyberBikeBody>>,
settings: Res<MovementSettings>,
) {
let mprops = mprops.single();
for (entity, mut tunneling, mut force, mut cgroups) in wheel_query.iter_mut() {
if tunneling.frames == 0 {
commands.entity(entity).remove::<Tunneling>();
force.force = Vec3::ZERO;
(cgroups.memberships, cgroups.filters) = BIKE_WHEEL_COLLISION_GROUP;
continue;
}
tunneling.frames -= 1;
force.force = tunneling.dir * settings.gravity * 1.5 * mprops.0.mass;
#[cfg(feature = "inspector")]
dbg!(&tunneling);
}
}
/// General velocity-based drag-force calculation; does not take orientation
/// into account.
pub(super) fn drag(mut query: Query<(&Velocity, &mut ExternalForce), With<CyberBikeBody>>) {
let (vels, mut forces) = query.single_mut();
if let Some(vel) = vels.linvel.try_normalize() {
let v2 = vels.linvel.length_squared();
forces.force -= vel * (v2 * 0.02);
}
let _angle = yaw_to_angle(input.yaw);
let _axle = axle.single();
//steering.align_orientation(bike, steering, rotation_difference, lagrange,
// compliance, dt)
}

View file

@ -44,11 +44,13 @@ pub(super) fn spawn_cyberbike(
let body =
Collider::capsule_endpoints(0.5, Vec3::new(0.0, 0.0, -0.65), Vec3::new(0.0, 0.0, 0.8));
let mass_props = MassPropertiesBundle::new_computed(&body, 0.6);
let bike = commands
.spawn(RigidBody::Dynamic)
.insert(spatialbundle)
.insert((
body,
mass_props.clone(),
bike_collision_group,
restitution,
friction,
@ -58,6 +60,7 @@ pub(super) fn spawn_cyberbike(
LinearVelocity::ZERO,
AngularVelocity::ZERO,
ExternalForce::ZERO,
ExternalTorque::ZERO,
))
.with_children(|rider| {
rider.spawn(SceneBundle {

View file

@ -6,10 +6,10 @@ use bevy::{
#[derive(Component)]
pub struct CyberBikeBody;
#[derive(Component)]
#[derive(Clone, Component)]
pub struct CyberSteering;
#[derive(Debug, Component)]
#[derive(Clone, Debug, Component)]
pub struct CyberWheel;
#[derive(Resource, Reflect)]

View file

@ -59,53 +59,18 @@ pub fn spawn_wheels(
..Default::default()
};
let suspension_damping = conf.damping;
let suspension_axis = if steering.is_some() {
rake_vec
} else {
Vec3::Y
};
let suspension_joint_builder = PrismaticJointBuilder::new(suspension_axis)
.local_anchor1(offset)
.limits(limits)
.motor_position(limits[0], stiffness, suspension_damping);
let suspension_joint = MultibodyJoint::new(bike, suspension_joint_builder);
let fork_rb_entity = commands
.spawn(RigidBody::Dynamic)
.insert(suspension_joint)
.insert(not_sleeping)
.id();
let axel_parent_entity = if let Some(steering) = steering {
let neck_builder =
RevoluteJointBuilder::new(rake_vec).local_anchor1(Vec3::new(0.0, 0.0, 0.1)); // this adds another 0.1m of trail
let neck_joint = MultibodyJoint::new(fork_rb_entity, neck_builder);
let neck = commands
.spawn(RigidBody::Dynamic)
.insert(neck_joint)
.insert(steering)
.insert(not_sleeping)
.id();
neck
} else {
fork_rb_entity
};
let axel_builder = RevoluteJointBuilder::new(Vec3::X);
let axel_joint = MultibodyJoint::new(axel_parent_entity, axel_builder);
let wheel_damping = Damping {
linear_damping: 0.8,
..Default::default()
};
commands.spawn(pbr_bundle).insert((
let wheel = commands
.spawn(pbr_bundle)
.insert((
collider,
wheel_damping,
ccd,
not_sleeping,
axel_joint,
wheels_collision_group,
friction,
CyberWheel,
@ -114,7 +79,35 @@ pub fn spawn_wheels(
SpatialBundle::default(),
RigidBody::Dynamic,
ColliderDensity(0.1),
));
AngularVelocity::ZERO,
ExternalTorque::ZERO,
))
.id();
let axel = if let Some(ref steering) = steering {
commands.spawn((RigidBody::Dynamic, ExternalForce::ZERO, steering.clone()))
} else {
commands.spawn((RigidBody::Dynamic, ExternalForce::ZERO))
}
.id();
let axel_joint = RevoluteJoint::new(axel, wheel).with_aligned_axis(Vec3::NEG_X);
commands.spawn(axel_joint);
// suspension and steering:
if steering.is_some() {
let joint = PrismaticJoint::new(axel, bike)
.with_free_axis(suspension_axis)
.with_local_anchor_1(Vec3::new(0.0, 0.0, 0.1))
.with_local_anchor_2(offset)
.with_limits(limits[0], limits[1]);
commands.spawn(joint);
} else {
let joint = PrismaticJoint::new(axel, bike)
.with_free_axis(suspension_axis)
.with_local_anchor_2(offset)
.with_limits(limits[0], limits[1]);
commands.spawn(joint);
};
}
}

View file

@ -1,4 +1,9 @@
use bevy::prelude::{App, Color, Plugin};
use avian3d::debug_render::PhysicsGizmos;
use bevy::{
color::Srgba,
gizmos::AppGizmoBuilder,
prelude::{App, Color, GizmoConfig, Plugin},
};
// use crate::planet::CyberPlanet;
@ -9,26 +14,16 @@ pub struct CyberGlamorPlugin;
impl Plugin for CyberGlamorPlugin {
fn build(&self, app: &mut App) {
{
use avian3d::render::{
DebugRenderMode, DebugRenderStyle, RapierDebugRenderPlugin,
};
let style = DebugRenderStyle {
multibody_joint_anchor_color: Color::GREEN.as_rgba_f32(),
let plugin = avian3d::debug_render::PhysicsDebugPlugin::default();
app.add_plugins(plugin).insert_gizmo_config(
PhysicsGizmos {
contact_point_color: Some(Srgba::GREEN.into()),
contact_normal_color: Some(Srgba::WHITE.into()),
..Default::default()
};
let mode = DebugRenderMode::CONTACTS
| DebugRenderMode::SOLVER_CONTACTS
| DebugRenderMode::JOINTS
| DebugRenderMode::RIGID_BODY_AXES;
let rplugin = RapierDebugRenderPlugin {
style,
always_on_top: true,
enabled: true,
mode,
};
app.add_plugin(rplugin);
},
GizmoConfig::default(),
);
}
}
}

View file

@ -14,22 +14,22 @@ pub(crate) struct InputState {
pub pitch: f32,
}
fn update_debug_cam(mut offset: ResMut<DebugCamOffset>, mut keys: ResMut<Input<KeyCode>>) {
fn update_debug_cam(mut offset: ResMut<DebugCamOffset>, mut keys: ResMut<ButtonInput<KeyCode>>) {
let keyset: HashSet<_> = keys.get_pressed().collect();
let shifted = keyset.contains(&KeyCode::LShift) || keyset.contains(&KeyCode::RShift);
let shifted = keyset.contains(&KeyCode::ShiftLeft) || keyset.contains(&KeyCode::ShiftRight);
for key in keyset {
match key {
KeyCode::Left => offset.rot -= 5.0,
KeyCode::Right => offset.rot += 5.0,
KeyCode::Up => {
KeyCode::ArrowLeft => offset.rot -= 5.0,
KeyCode::ArrowRight => offset.rot += 5.0,
KeyCode::ArrowUp => {
if shifted {
offset.alt += 0.5;
} else {
offset.dist -= 0.5;
}
}
KeyCode::Down => {
KeyCode::ArrowDown => {
if shifted {
offset.alt -= 0.5;
} else {
@ -41,16 +41,17 @@ fn update_debug_cam(mut offset: ResMut<DebugCamOffset>, mut keys: ResMut<Input<K
}
if keys.get_just_released().len() > 0 {
let unpressed = keys.just_released(KeyCode::LShift) || keys.just_released(KeyCode::RShift);
let unpressed =
keys.just_released(KeyCode::ShiftLeft) || keys.just_released(KeyCode::ShiftRight);
keys.reset_all();
if shifted && !unpressed {
keys.press(KeyCode::LShift);
keys.press(KeyCode::ShiftLeft);
}
}
}
fn update_input(mut events: EventReader<GamepadEvent>, mut istate: ResMut<InputState>) {
for pad_event in events.iter() {
for pad_event in events.read() {
match pad_event {
GamepadEvent::Button(button_event) => {
let GamepadButtonChangedEvent {
@ -92,7 +93,6 @@ pub struct CyberInputPlugin;
impl Plugin for CyberInputPlugin {
fn build(&self, app: &mut App) {
app.init_resource::<InputState>()
.add_system(update_input)
.add_system(update_debug_cam);
.add_systems(Update, (update_input, update_debug_cam));
}
}

View file

@ -1,8 +1,8 @@
use bevy::{
prelude::{shape::Icosphere, *},
render::{color::Color, mesh::Indices},
};
use avian3d::prelude::*;
use bevy::{
prelude::*,
render::{mesh::Indices, render_asset::RenderAssetUsages},
};
use hexasphere::shapes::IcoSphere;
use noise::{HybridMulti, NoiseFn, SuperSimplex};
use rand::{Rng, SeedableRng};
@ -20,20 +20,12 @@ fn spawn_planet(
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
//let color = Color::rgb(0.74, 0.5334, 0.176);
let isphere = Icosphere {
radius: PLANET_RADIUS,
subdivisions: 88,
};
let (mesh, shape) = gen_planet(isphere);
let pbody = (RigidBody::Fixed, Ccd { enabled: true });
let (mesh, shape) = gen_planet(PLANET_RADIUS);
let pcollide = (
shape,
Friction {
coefficient: 1.2,
static_coefficient: 1.2,
..Default::default()
},
Restitution::new(0.8),
@ -42,18 +34,22 @@ fn spawn_planet(
commands
.spawn(PbrBundle {
mesh: meshes.add(mesh),
material: materials.add(Color::WHITE.into()),
material: materials.add(Color::WHITE),
..Default::default()
})
.insert(pbody)
.insert(pcollide)
.insert(CyberPlanet);
.insert((
RigidBody::Static,
pcollide,
CyberPlanet,
CollisionLayers::new(u32::MAX, u32::MAX),
CollisionMargin(0.2),
));
}
pub struct CyberPlanetPlugin;
impl Plugin for CyberPlanetPlugin {
fn build(&self, app: &mut App) {
app.add_startup_system(spawn_planet);
app.add_systems(Startup, spawn_planet);
}
}
@ -61,10 +57,10 @@ impl Plugin for CyberPlanetPlugin {
// utils
//---------------------------------------------------------------------
fn gen_planet(sphere: Icosphere) -> (Mesh, Collider) {
fn gen_planet(radius: f32) -> (Mesh, Collider) {
// straight-up stolen from Bevy's impl of Mesh from Icosphere, so I can do the
// displacement before normals are calculated.
let generated = IcoSphere::new(sphere.subdivisions, |point| {
let generated = IcoSphere::new(79, |point| {
let inclination = point.y.acos();
let azimuth = point.z.atan2(point.x);
@ -90,7 +86,7 @@ fn gen_planet(sphere: Icosphere) -> (Mesh, Collider) {
let points = noisy_points
.iter()
.map(|&p| (Vec3::from_slice(&p) * sphere.radius).into())
.map(|&p| (Vec3::from_slice(&p) * radius).into())
.collect::<Vec<[f32; 3]>>();
for p in &points {
@ -108,10 +104,13 @@ fn gen_planet(sphere: Icosphere) -> (Mesh, Collider) {
}
let indices = Indices::U32(indices);
let collider = Collider::trimesh(points.iter().map(|p| Vect::from_slice(p)).collect(), idxs);
let collider = Collider::trimesh(points.iter().map(|p| Vec3::from_slice(p)).collect(), idxs);
let mut mesh = Mesh::new(PrimitiveTopology::TriangleList);
mesh.set_indices(Some(indices));
let mut mesh = Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetUsages::default(),
);
mesh.insert_indices(indices);
mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, points);
//mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs);
mesh.duplicate_vertices();
@ -129,7 +128,9 @@ fn gen_planet(sphere: Icosphere) -> (Mesh, Collider) {
let l = 0.41;
let jitter = rng.gen_range(-0.0..=360.0f32);
let h = jitter;
let color = Color::hsl(h, PLANET_SATURATION, l).as_linear_rgba_f32();
let color = Color::hsl(h, PLANET_SATURATION, l)
.to_linear()
.to_f32_array();
for _ in 0..3 {
colors.push(color);
}

View file

@ -1,10 +1,13 @@
use bevy::prelude::{
use avian3d::prelude::LinearVelocity;
use bevy::{
app::{Startup, Update},
prelude::{
AlignSelf, App, AssetServer, Color, Commands, Component, Plugin, Query, Res, Style, Text,
TextBundle, TextSection, TextStyle, Transform, With,
},
};
#[cfg(feature = "inspector")]
use bevy_inspector_egui::quick::WorldInspectorPlugin;
use avian3d::prelude::Velocity;
use crate::bike::CyberBikeBody;
@ -26,7 +29,7 @@ fn setup_ui(mut commands: Commands, asset_server: Res<AssetServer>) {
style: TextStyle {
font: asset_server.load("fonts/FiraMono-Medium.ttf"),
font_size: 40.0,
color: Color::GOLD,
color: Color::srgba_u8(255, 215, 0, 230),
},
}],
..Default::default()
@ -37,12 +40,12 @@ fn setup_ui(mut commands: Commands, asset_server: Res<AssetServer>) {
}
fn update_ui(
state_query: Query<(&Velocity, &Transform), With<CyberBikeBody>>,
state_query: Query<(&LinearVelocity, &Transform), With<CyberBikeBody>>,
mut text_query: Query<&mut Text, With<UpText>>,
) {
let mut text = text_query.single_mut();
let (velocity, xform) = state_query.single();
let speed = velocity.linvel.dot(xform.forward());
let speed = velocity.dot(*xform.forward());
text.sections[0].value = format!("spd: {:.2}", speed);
}
@ -53,6 +56,7 @@ impl Plugin for CyberUIPlugin {
#[cfg(feature = "inspector")]
app.add_plugin(WorldInspectorPlugin);
app.add_startup_system(setup_ui).add_system(update_ui);
app.add_systems(Startup, setup_ui)
.add_systems(Update, update_ui);
}
}