Compare commits

..

No commits in common. "ecfa9b586428f7afbeb6b8af5a0a41fc6cea795b" and "aef303ca71e6e15869700545cc91fcaae8cb8ed7" have entirely different histories.

13 changed files with 261 additions and 195 deletions

View file

@ -37,7 +37,7 @@ impl Default for MovementSettings {
fn default() -> Self { fn default() -> Self {
Self { Self {
accel: 20.0, accel: 20.0,
gravity: 9.8, gravity: 4.8,
} }
} }
} }
@ -53,9 +53,9 @@ pub struct CatControllerSettings {
impl Default for CatControllerSettings { impl Default for CatControllerSettings {
fn default() -> Self { fn default() -> Self {
Self { Self {
kp: 1200.0, kp: 80.0,
kd: 10.0, kd: 10.0,
ki: 50.0, ki: 0.4,
} }
} }
} }

View file

@ -1,6 +1,6 @@
use avian3d::prelude::{PhysicsPlugins, SubstepCount}; use avian3d::prelude::{PhysicsPlugins, SubstepCount};
use bevy::{ use bevy::{
app::FixedUpdate, app::Update,
diagnostic::FrameTimeDiagnosticsPlugin, diagnostic::FrameTimeDiagnosticsPlugin,
ecs::reflect::ReflectResource, ecs::reflect::ReflectResource,
prelude::{App, IntoSystemConfigs, Plugin, Resource}, prelude::{App, IntoSystemConfigs, Plugin, Resource},
@ -29,10 +29,15 @@ impl Plugin for CyberActionPlugin {
.register_type::<CyberLean>() .register_type::<CyberLean>()
.register_type::<CatControllerSettings>() .register_type::<CatControllerSettings>()
.add_plugins((PhysicsPlugins::default(), FrameTimeDiagnosticsPlugin)) .add_plugins((PhysicsPlugins::default(), FrameTimeDiagnosticsPlugin))
.insert_resource(SubstepCount(12)) .insert_resource(SubstepCount(24))
.add_systems( .add_systems(
FixedUpdate, Update,
(set_gravity, calculate_lean, apply_lean).chain(), (
gravity,
#[cfg(feature = "inspector")]
debug_bodies,
(cyber_lean, suspension, falling_cat, input_forces).after(clear_forces),
),
); );
} }
} }

View file

@ -2,15 +2,18 @@ use std::f32::consts::{FRAC_PI_3, FRAC_PI_4};
use avian3d::{ use avian3d::{
dynamics::solver::xpbd::AngularConstraint, dynamics::solver::xpbd::AngularConstraint,
prelude::{ExternalTorque, FixedJoint, Gravity, LinearVelocity, RigidBodyQuery}, prelude::{
ColliderMassProperties, ExternalForce, ExternalTorque, Gravity, LinearVelocity,
PrismaticJoint, RigidBodyQuery,
},
}; };
#[cfg(feature = "inspector")] #[cfg(feature = "inspector")]
use bevy::prelude::Gizmos; use bevy::prelude::Entity;
use bevy::prelude::{Quat, Query, Res, ResMut, Time, Transform, Vec3, With, Without}; use bevy::prelude::{Quat, Query, Res, ResMut, Time, Transform, Vec3, With, Without};
use super::{CatControllerSettings, CatControllerState, CyberLean, MovementSettings}; use super::{CatControllerSettings, CatControllerState, CyberLean, MovementSettings};
use crate::{ use crate::{
bike::{CyberBikeBody, CyberFork, CyberSteering, CyberWheel}, bike::{CyberBikeBody, CyberFork, CyberSpring, CyberSteering, CyberWheel, WheelConfig},
input::InputState, input::InputState,
}; };
@ -35,17 +38,47 @@ fn rotate_point(pt: &Vec3, rot: &Quat) -> Vec3 {
} }
/// The gravity vector points from the cyberbike to the center of the planet. /// The gravity vector points from the cyberbike to the center of the planet.
pub(super) fn set_gravity( pub(super) fn gravity(
xform: Query<&Transform, With<CyberBikeBody>>, xform: Query<&Transform, With<CyberBikeBody>>,
settings: Res<MovementSettings>, settings: Res<MovementSettings>,
mut gravity: ResMut<Gravity>, mut gravity: ResMut<Gravity>,
) { ) {
let xform = xform.single(); let xform = xform.single();
*gravity = Gravity(xform.translation.normalize() * -settings.gravity); *gravity = Gravity(xform.translation.normalize() * -settings.gravity);
// clear all external forces
}
pub(super) fn clear_forces(mut forces: Query<(&mut ExternalForce, &mut ExternalTorque)>) {
for (mut force, mut torque) in forces.iter_mut() {
force.clear();
torque.clear();
}
}
pub(super) fn suspension(
movment_settings: Res<MovementSettings>,
wheel_config: Res<WheelConfig>,
mass: Query<&ColliderMassProperties, With<CyberBikeBody>>,
mut axels: Query<(&mut ExternalForce, &CyberSpring, &Transform)>,
) {
let mass = if let Ok(mass) = mass.get_single() {
mass.mass.0
} else {
1.0
};
let gravity = movment_settings.gravity;
let mag = -wheel_config.stiffness * mass * gravity;
for (mut force, spring, xform) in axels.iter_mut() {
let spring = mag * spring.0;
let spring = xform.translation + spring;
let _ = force.apply_force(spring);
}
} }
/// The desired lean angle, given steering input and speed. /// The desired lean angle, given steering input and speed.
pub(super) fn calculate_lean( pub(super) fn cyber_lean(
bike_state: Query<(&LinearVelocity, &Transform), With<CyberBikeBody>>, bike_state: Query<(&LinearVelocity, &Transform), With<CyberBikeBody>>,
wheels: Query<&Transform, With<CyberWheel>>, wheels: Query<&Transform, With<CyberWheel>>,
input: Res<InputState>, input: Res<InputState>,
@ -63,7 +96,7 @@ pub(super) fn calculate_lean(
let v2_r = v_squared / radius; let v2_r = v_squared / radius;
let tan_theta = (v2_r / gravity).clamp(-FRAC_PI_3, FRAC_PI_3); let tan_theta = (v2_r / gravity).clamp(-FRAC_PI_3, FRAC_PI_3);
if tan_theta.is_normal() { if tan_theta.is_finite() && !tan_theta.is_subnormal() {
lean.lean = tan_theta.atan().clamp(-FRAC_PI_4, FRAC_PI_4); lean.lean = tan_theta.atan().clamp(-FRAC_PI_4, FRAC_PI_4);
} else { } else {
lean.lean = 0.0; lean.lean = 0.0;
@ -71,12 +104,11 @@ pub(super) fn calculate_lean(
} }
/// PID-based controller for stabilizing attitude; keeps the cyberbike upright. /// PID-based controller for stabilizing attitude; keeps the cyberbike upright.
pub(super) fn apply_lean( pub(super) fn falling_cat(
mut bike_query: Query<(&Transform, &mut ExternalTorque, &mut CatControllerState)>, mut bike_query: Query<(&Transform, &mut ExternalTorque, &mut CatControllerState)>,
time: Res<Time>, time: Res<Time>,
settings: Res<CatControllerSettings>, settings: Res<CatControllerSettings>,
lean: Res<CyberLean>, lean: Res<CyberLean>,
#[cfg(feature = "inspector")] mut gizmos: Gizmos,
) { ) {
let (xform, mut torque, 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 world_up = xform.translation.normalize();
@ -93,53 +125,73 @@ pub(super) fn apply_lean(
let (derivative, integral) = control_vars.update_roll(roll_error, time.delta_seconds()); let (derivative, integral) = control_vars.update_roll(roll_error, time.delta_seconds());
let mag = let mag =
(settings.kp * roll_error) + (settings.ki * integral) + (settings.kd * derivative); (settings.kp * roll_error) + (settings.ki * integral) + (settings.kd * derivative);
if mag.is_normal() { if mag.is_finite() {
let tork = mag * *xform.back(); torque.apply_torque(*xform.back() * mag);
torque.apply_torque(tork);
#[cfg(feature = "inspector")]
gizmos.arrow(
xform.translation + Vec3::Y,
xform.translation + tork + Vec3::Y,
bevy::color::Color::WHITE,
);
} }
} }
} }
/// Apply forces to the cyberbike as a result of input. /// Apply forces to the cyberbike as a result of input.
pub(super) fn apply_inputs( pub(super) fn input_forces(
settings: Res<MovementSettings>, settings: Res<MovementSettings>,
input: Res<InputState>, input: Res<InputState>,
time: Res<Time>, time: Res<Time>,
fork: Query<(&FixedJoint, &CyberFork), With<CyberSteering>>, fork: Query<&PrismaticJoint, With<CyberFork>>,
mut hub: Query<(RigidBodyQuery, &Transform), With<CyberSteering>>, mut axle: Query<(RigidBodyQuery, &Transform), With<CyberSteering>>,
mut braking_query: Query<(&Transform, &mut ExternalTorque), With<CyberWheel>>, mut braking_query: Query<&mut ExternalTorque, With<CyberWheel>>,
mut body_query: Query<RigidBodyQuery, (With<CyberBikeBody>, Without<CyberSteering>)>, mut body_query: Query<
(
RigidBodyQuery,
&Transform,
&ColliderMassProperties,
&mut ExternalForce,
),
(With<CyberBikeBody>, Without<CyberSteering>),
>,
) { ) {
let Ok(mut bike) = body_query.get_single_mut() else { let Ok((mut bike, xform, mass, mut forces)) = body_query.get_single_mut() else {
bevy::log::debug!("no bike body found"); bevy::log::debug!("no bike body found");
return; return;
}; };
let dt = time.delta_seconds(); let dt = time.delta_seconds();
// brake + thrust // thrust
for (xform, mut motor) in braking_query.iter_mut() { let thrust = xform.forward() * input.throttle * settings.accel;
let factor = input.throttle * settings.accel; let point = xform.translation + *xform.back();
let target = dt * factor; let _ = *forces.apply_force_at_point(dt * thrust, point, mass.center_of_mass.0);
let torque = target * *xform.right();
// brake + thrust
for mut motor in braking_query.iter_mut() {
let factor = if input.brake {
500.00
} else {
input.throttle * settings.accel
};
let speed = if input.brake { 0.0 } else { -70.0 };
let target = dt * factor * speed;
let torque = target * Vec3::X;
motor.apply_torque(torque); motor.apply_torque(torque);
} }
// steering // steering
let _angle = yaw_to_angle(input.yaw); let _angle = yaw_to_angle(input.yaw);
let (mut axle, xform) = hub.single_mut(); let (mut axle, xform) = axle.single_mut();
let cur_rot = xform.rotation; let cur_rot = xform.rotation;
let (fork, CyberFork(axis)) = fork.single(); let fork = fork.single();
let new = Quat::from_axis_angle(*axis, _angle); let axis = fork.free_axis.normalize();
let new = Quat::from_axis_angle(axis, _angle);
let diff = (new - cur_rot).to_scaled_axis(); let diff = (new - cur_rot).to_scaled_axis();
let mut lagrange = 1.0; let mut compliance = 1.0;
fork.align_orientation(&mut axle, &mut bike, diff, &mut lagrange, 0.0, dt); fork.align_orientation(&mut axle, &mut bike, diff, &mut compliance, 1.0, dt);
}
#[cfg(feature = "inspector")]
pub(super) fn debug_bodies(bodies: Query<(Entity, RigidBodyQuery)>) {
for (entity, rbq) in bodies.iter().filter(|(_, r)| r.rb.is_dynamic()) {
let line = format!("{entity:?} at {:?}", rbq.current_position());
bevy::log::info!(line);
}
} }

View file

@ -1,12 +1,11 @@
use avian3d::prelude::*; use avian3d::prelude::*;
use bevy::{ use bevy::{
core::Name,
prelude::{AssetServer, BuildChildren, Commands, Quat, Res, SpatialBundle, Transform, Vec3}, prelude::{AssetServer, BuildChildren, Commands, Quat, Res, SpatialBundle, Transform, Vec3},
scene::SceneBundle, scene::SceneBundle,
}; };
use super::{spawn_wheels, CyberBikeBody, Meshterial, WheelConfig}; use super::{spawn_wheels, CyberBikeBody, Meshterial, WheelConfig, BIKE_BODY_COLLISION_GROUP};
use crate::{action::CatControllerState, planet::PLANET_RADIUS, ColliderGroups}; use crate::{action::CatControllerState, planet::PLANET_RADIUS};
pub(super) fn spawn_cyberbike( pub(super) fn spawn_cyberbike(
mut commands: Commands, mut commands: Commands,
@ -33,8 +32,8 @@ pub(super) fn spawn_cyberbike(
..Default::default() ..Default::default()
}; };
let bike_collision_group = let (membership, filter) = BIKE_BODY_COLLISION_GROUP;
CollisionLayers::new(ColliderGroups::BikeBody, ColliderGroups::Planet); let bike_collision_group = CollisionLayers::new(membership, filter);
let scene = asset_server.load("cb-no-y_up.glb#Scene0"); let scene = asset_server.load("cb-no-y_up.glb#Scene0");
@ -43,7 +42,6 @@ pub(super) fn spawn_cyberbike(
let bike = commands let bike = commands
.spawn(SpatialBundle::from_transform(xform)) .spawn(SpatialBundle::from_transform(xform))
.insert(( .insert((
Name::new("bike body"),
RigidBody::Dynamic, RigidBody::Dynamic,
body_collider, body_collider,
bike_collision_group, bike_collision_group,
@ -52,12 +50,13 @@ pub(super) fn spawn_cyberbike(
SleepingDisabled, SleepingDisabled,
CyberBikeBody, CyberBikeBody,
CatControllerState::default(), CatControllerState::default(),
ColliderDensity(20.0), ColliderDensity(0.6),
LinearDamping(0.1), LinearDamping(0.1),
AngularDamping(2.0), AngularDamping(2.0),
LinearVelocity::ZERO, LinearVelocity::ZERO,
AngularVelocity::ZERO, AngularVelocity::ZERO,
ExternalTorque::ZERO.with_persistence(false), ExternalForce::ZERO,
ExternalTorque::ZERO,
)) ))
.with_children(|rider| { .with_children(|rider| {
rider.spawn(SceneBundle { rider.spawn(SceneBundle {
@ -67,7 +66,5 @@ pub(super) fn spawn_cyberbike(
}) })
.id(); .id();
bevy::log::info!("bike body: {bike:?}");
spawn_wheels(&mut commands, bike, &xform, &wheel_conf, &mut meshterials); spawn_wheels(&mut commands, bike, &xform, &wheel_conf, &mut meshterials);
} }

View file

@ -14,7 +14,10 @@ pub struct CyberSteering;
pub struct CyberWheel; pub struct CyberWheel;
#[derive(Clone, Debug, Component)] #[derive(Clone, Debug, Component)]
pub struct CyberFork(pub Vec3); pub struct CyberSpring(pub Vec3);
#[derive(Clone, Debug, Component)]
pub struct CyberFork;
#[derive(Resource, Reflect)] #[derive(Resource, Reflect)]
#[reflect(Resource)] #[reflect(Resource)]
@ -37,15 +40,15 @@ impl Default for WheelConfig {
Self { Self {
front_forward: 0.8, front_forward: 0.8,
rear_back: 1.0, rear_back: 1.0,
y: -0.5, y: -0.1,
limits: [-0.5, 0.1], limits: [-0.5, 0.1],
stiffness: 3.0, stiffness: 3.0,
damping: 8.0, damping: 8.0,
radius: 0.40, radius: 0.25,
thickness: 0.15, thickness: 0.11,
friction: 0.9, friction: 1.2,
restitution: 0.5, restitution: 0.95,
density: 30.0, density: 0.05,
} }
} }
} }

View file

@ -10,8 +10,8 @@ use bevy::{
pub(crate) use self::components::*; pub(crate) use self::components::*;
use self::{body::spawn_cyberbike, wheels::spawn_wheels}; use self::{body::spawn_cyberbike, wheels::spawn_wheels};
pub const BIKE_BODY_COLLISION_GROUP: (u32, u32) = (1, 1); pub const BIKE_BODY_COLLISION_GROUP: (u32, u32) = (0b01, 0b01);
pub const BIKE_WHEEL_COLLISION_GROUP: (u32, u32) = (2, 2); pub const BIKE_WHEEL_COLLISION_GROUP: (u32, u32) = (0b10, 0b10);
type Meshterial<'a> = ( type Meshterial<'a> = (
ResMut<'a, Assets<Mesh>>, ResMut<'a, Assets<Mesh>>,

View file

@ -1,8 +1,10 @@
use avian3d::prelude::*; use avian3d::prelude::*;
use bevy::prelude::*; use bevy::prelude::{Torus as Tire, *};
use super::{CyberFork, CyberSteering, CyberWheel, Meshterial, WheelConfig}; use super::{
use crate::ColliderGroups; CyberFork, CyberSpring, CyberSteering, CyberWheel, Meshterial, WheelConfig,
BIKE_WHEEL_COLLISION_GROUP,
};
pub fn spawn_wheels( pub fn spawn_wheels(
commands: &mut Commands, commands: &mut Commands,
@ -11,11 +13,21 @@ pub fn spawn_wheels(
conf: &WheelConfig, conf: &WheelConfig,
meshterials: &mut Meshterial, meshterials: &mut Meshterial,
) { ) {
let (membership, filter) = BIKE_WHEEL_COLLISION_GROUP;
let wheels_collision_group = CollisionLayers::new(membership, filter);
let wheel_y = conf.y; let wheel_y = conf.y;
let not_sleeping = SleepingDisabled;
let ccd = SweptCcd::NON_LINEAR.include_dynamic(false);
let limits = conf.limits;
let (meshes, materials) = meshterials; let (meshes, materials) = meshterials;
let rake_vec: Vec3 = Vec3::new(0.0, 1.0, 0.57).normalize(); // about 30 degrees of rake let rake_vec: Vec3 = Vec3::new(0.0, 1.0, 0.57).normalize(); // about 30 degrees of rake
let friction = Friction {
dynamic_coefficient: conf.friction,
static_coefficient: conf.friction,
combine_rule: CoefficientCombine::Average,
};
let mut wheel_poses = Vec::with_capacity(2); let mut wheel_poses = Vec::with_capacity(2);
// front // front
@ -34,34 +46,107 @@ pub fn spawn_wheels(
wheel_poses.push((offset, None)); wheel_poses.push((offset, None));
} }
let (mesh, collider) = gen_tire(conf);
for (offset, steering) in wheel_poses { for (offset, steering) in wheel_poses {
let hub = wheels_helper( let (mesh, collider) = gen_tires(conf);
commands,
meshes,
materials,
offset + xform.translation,
mesh.clone(),
collider.clone(),
conf,
);
let fork = commands let material = StandardMaterial {
.spawn(FixedJoint::new(hub, bike).with_local_anchor_2(offset)) base_color: Color::linear_rgba(0.01, 0.01, 0.01, 1.0),
alpha_mode: AlphaMode::Opaque,
perceptual_roughness: 0.5,
..Default::default()
};
let pbr_bundle = PbrBundle {
material: materials.add(material),
mesh: meshes.add(mesh),
..Default::default()
};
let suspension_axis = if steering.is_some() {
rake_vec
} else {
Vec3::Y
};
let wheel = commands
.spawn(pbr_bundle)
.insert((
collider,
ccd,
not_sleeping,
wheels_collision_group,
friction,
CyberWheel,
ExternalForce::default(),
Restitution::new(conf.restitution),
SpatialBundle::default(),
RigidBody::Dynamic,
ColliderDensity(0.1),
AngularVelocity::ZERO,
ExternalTorque::ZERO,
))
.insert(SpatialBundle::from_transform(
xform.with_translation(xform.translation + offset),
))
.id(); .id();
if let Some(steering) = steering {
let axis = CyberFork(rake_vec); let spring = CyberSpring(suspension_axis);
commands.entity(fork).insert((steering, axis)); let axel = if let Some(steering) = steering {
commands.entity(hub).insert(steering); commands.spawn((
RigidBody::Dynamic,
ExternalForce::ZERO,
steering,
spring,
Collider::sphere(0.1),
ColliderDensity(0.1),
CollisionLayers::from_bits(0, 0),
))
} else {
commands.spawn((
RigidBody::Dynamic,
ExternalForce::ZERO,
spring,
Collider::sphere(0.1),
ColliderDensity(0.1),
CollisionLayers::from_bits(0, 0),
))
} }
.insert(SpatialBundle::from_transform(
xform.with_translation(xform.translation + offset),
))
.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, CyberFork));
} 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);
};
} }
} }
fn gen_tire(conf: &WheelConfig) -> (Mesh, Collider) { // do mesh shit
let outer_radius = conf.radius; fn gen_tires(conf: &WheelConfig) -> (Mesh, Collider) {
let inner_radius = conf.radius - conf.thickness; let wheel_rad = conf.radius;
let mut tire_mesh: Mesh = Torus::new(inner_radius, outer_radius).into(); let tire_thickness = conf.thickness;
let tire_verts = tire_mesh let tire = Tire {
minor_radius: tire_thickness,
major_radius: wheel_rad,
};
let mut mesh = Mesh::from(tire);
let tire_verts = mesh
.attribute(Mesh::ATTRIBUTE_POSITION) .attribute(Mesh::ATTRIBUTE_POSITION)
.unwrap() .unwrap()
.as_float3() .as_float3()
@ -75,70 +160,15 @@ fn gen_tire(conf: &WheelConfig) -> (Mesh, Collider) {
p.to_array() p.to_array()
}) })
.collect::<Vec<[f32; 3]>>(); .collect::<Vec<[f32; 3]>>();
tire_mesh.remove_attribute(Mesh::ATTRIBUTE_POSITION); mesh.remove_attribute(Mesh::ATTRIBUTE_POSITION);
tire_mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, tire_verts); mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, tire_verts);
let collider = Collider::convex_hull_from_mesh(&tire_mesh).unwrap(); let mut idxs = Vec::new();
let indices = mesh.indices().unwrap().iter().collect::<Vec<_>>();
for idx in indices.as_slice().chunks_exact(3) {
idxs.push([idx[0] as u32, idx[1] as u32, idx[2] as u32]);
}
let wheel_collider = Collider::convex_decomposition_from_mesh(&mesh).unwrap();
(tire_mesh, collider) (mesh, wheel_collider)
}
fn wheels_helper(
commands: &mut Commands,
meshes: &mut ResMut<Assets<Mesh>>,
materials: &mut ResMut<Assets<StandardMaterial>>,
position: Vec3,
tire_mesh: Mesh,
collider: Collider,
conf: &WheelConfig,
) -> Entity {
let wheel_material = &StandardMaterial {
base_color: Color::srgb(0.01, 0.01, 0.01),
alpha_mode: AlphaMode::Opaque,
perceptual_roughness: 0.5,
..Default::default()
};
let xform = Transform::from_translation(position);
let hub_mesh: Mesh = Sphere::new(0.1).into();
let hub = commands
.spawn((
Name::new("hub"),
RigidBody::Dynamic,
MassPropertiesBundle::new_computed(&Collider::sphere(0.1), 200.0),
PbrBundle {
mesh: meshes.add(hub_mesh),
material: materials.add(wheel_material.clone()),
transform: xform,
..Default::default()
},
))
.id();
let tire = commands
.spawn((
Name::new("tire"),
PbrBundle {
mesh: meshes.add(tire_mesh),
material: materials.add(wheel_material.clone()),
transform: xform,
..Default::default()
},
CyberWheel,
RigidBody::Dynamic,
collider,
Friction::new(conf.friction),
Restitution::new(conf.restitution),
ColliderDensity(conf.density),
CollisionLayers::new(ColliderGroups::Wheels, ColliderGroups::Planet),
ExternalTorque::ZERO.with_persistence(false),
CollisionMargin(0.05),
SweptCcd::NON_LINEAR,
))
.id();
// connect hubs and tires to make wheels
commands.spawn(RevoluteJoint::new(hub, tire).with_aligned_axis(Vec3::X));
hub
} }

View file

@ -1,6 +1,6 @@
use bevy::prelude::*; use bevy::prelude::*;
use crate::{bike::CyberBikeBody, input::InputState, ui::setup_ui}; use crate::{bike::CyberBikeBody, input::InputState};
// 85 degrees in radians // 85 degrees in radians
const MAX_PITCH: f32 = 1.48353; const MAX_PITCH: f32 = 1.48353;
@ -38,25 +38,22 @@ impl CyberCameras {
} }
} }
fn setup_cybercams(mut commands: Commands, asset_server: Res<AssetServer>) { fn setup_cybercams(mut commands: Commands) {
let hero_projection = PerspectiveProjection { let hero_projection = PerspectiveProjection {
fov: std::f32::consts::FRAC_PI_3, fov: std::f32::consts::FRAC_PI_3,
..Default::default() ..Default::default()
}; };
let id = commands commands
.spawn(Camera3dBundle { .spawn(Camera3dBundle {
projection: bevy::render::camera::Projection::Perspective(hero_projection), projection: bevy::render::camera::Projection::Perspective(hero_projection),
..Default::default() ..Default::default()
}) })
.insert(CyberCameras::Hero) .insert(CyberCameras::Hero);
.id();
commands commands
.spawn(Camera3dBundle::default()) .spawn(Camera3dBundle::default())
.insert(CyberCameras::Debug); .insert(CyberCameras::Debug);
setup_ui(commands, asset_server, id);
} }
fn follow_cyberbike( fn follow_cyberbike(
@ -87,7 +84,7 @@ fn follow_cyberbike(
cam_xform.rotate(Quat::from_axis_angle(*axis, angle)); cam_xform.rotate(Quat::from_axis_angle(*axis, angle));
} }
CyberCameras::Debug => { CyberCameras::Debug => {
let mut ncx = Transform::from_translation(bike_xform.translation); let mut ncx = bike_xform.to_owned();
ncx.rotate(Quat::from_axis_angle(up, offset.rot.to_radians())); ncx.rotate(Quat::from_axis_angle(up, offset.rot.to_radians()));
ncx.translation += ncx.forward() * offset.dist; ncx.translation += ncx.forward() * offset.dist;
ncx.translation += ncx.up() * offset.alt; ncx.translation += ncx.up() * offset.alt;
@ -100,15 +97,12 @@ fn follow_cyberbike(
fn update_active_camera( fn update_active_camera(
state: Res<State<CyberCameras>>, state: Res<State<CyberCameras>>,
mut cameras: Query<(Entity, &mut Camera, &CyberCameras)>, mut query: Query<(&mut Camera, &CyberCameras)>,
mut target: Query<&mut TargetCamera>,
) { ) {
let mut target = target.single_mut();
// find the camera with the current state, set it as the ActiveCamera // find the camera with the current state, set it as the ActiveCamera
cameras.iter_mut().for_each(|(ent, mut cam, cyber)| { query.iter_mut().for_each(|(mut cam, cyber)| {
if cyber.eq(state.get()) { if cyber.eq(&state.get()) {
cam.is_active = true; cam.is_active = true;
*target = TargetCamera(ent);
} else { } else {
cam.is_active = false; cam.is_active = false;
} }

View file

@ -2,7 +2,6 @@ use avian3d::debug_render::PhysicsGizmos;
use bevy::{ use bevy::{
color::Srgba, color::Srgba,
gizmos::AppGizmoBuilder, gizmos::AppGizmoBuilder,
math::Vec3,
prelude::{App, Color, GizmoConfig, Plugin}, prelude::{App, Color, GizmoConfig, Plugin},
}; };
@ -21,8 +20,6 @@ impl Plugin for CyberGlamorPlugin {
PhysicsGizmos { PhysicsGizmos {
contact_point_color: Some(Srgba::GREEN.into()), contact_point_color: Some(Srgba::GREEN.into()),
contact_normal_color: Some(Srgba::WHITE.into()), contact_normal_color: Some(Srgba::WHITE.into()),
hide_meshes: true,
axis_lengths: Some(Vec3::ZERO),
..Default::default() ..Default::default()
}, },
GizmoConfig::default(), GizmoConfig::default(),

View file

@ -1,4 +1,3 @@
use avian3d::prelude::PhysicsLayer;
use bevy::{ use bevy::{
prelude::{Query, Vec3, Window, With}, prelude::{Query, Vec3, Window, With},
window::PrimaryWindow, window::PrimaryWindow,
@ -13,13 +12,6 @@ pub mod lights;
pub mod planet; pub mod planet;
pub mod ui; pub mod ui;
#[derive(PhysicsLayer)]
pub enum ColliderGroups {
BikeBody,
Wheels,
Planet,
}
pub fn disable_mouse_trap(mut window: Query<&mut Window, With<PrimaryWindow>>) { pub fn disable_mouse_trap(mut window: Query<&mut Window, With<PrimaryWindow>>) {
let mut window = window.get_single_mut().unwrap(); let mut window = window.get_single_mut().unwrap();
window.cursor.grab_mode = bevy::window::CursorGrabMode::None; window.cursor.grab_mode = bevy::window::CursorGrabMode::None;

View file

@ -2,7 +2,7 @@ use bevy::{pbr::CascadeShadowConfigBuilder, prelude::*};
use crate::planet::PLANET_RADIUS; use crate::planet::PLANET_RADIUS;
pub const LIGHT_RANGE: f32 = 900.0; pub const LIGHT_RANGE: f32 = 90.0;
static BLUE: Color = Color::linear_rgb(0.0, 0.0, 1.0); static BLUE: Color = Color::linear_rgb(0.0, 0.0, 1.0);
static PINK: Color = Color::linear_rgb(199.0 / 255.0, 21.0 / 255.0, 113.0 / 255.0); static PINK: Color = Color::linear_rgb(199.0 / 255.0, 21.0 / 255.0, 113.0 / 255.0);
@ -13,7 +13,7 @@ fn spawn_static_lights(
mut materials: ResMut<Assets<StandardMaterial>>, mut materials: ResMut<Assets<StandardMaterial>>,
) { ) {
let pink_light = PointLight { let pink_light = PointLight {
intensity: 1_000_000.0, intensity: 1_00.0,
range: LIGHT_RANGE, range: LIGHT_RANGE,
color: PINK, color: PINK,
radius: 1.0, radius: 1.0,
@ -22,7 +22,7 @@ fn spawn_static_lights(
}; };
let blue_light = PointLight { let blue_light = PointLight {
intensity: 1_000_000.0, intensity: 1_000.0,
range: LIGHT_RANGE, range: LIGHT_RANGE,
color: BLUE, color: BLUE,
radius: 1.0, radius: 1.0,
@ -32,7 +32,7 @@ fn spawn_static_lights(
commands.insert_resource(AmbientLight { commands.insert_resource(AmbientLight {
color: Color::WHITE, color: Color::WHITE,
brightness: 8_000.0, brightness: 0.2,
}); });
let _cascade_shadow_config = CascadeShadowConfigBuilder { let _cascade_shadow_config = CascadeShadowConfigBuilder {

View file

@ -25,9 +25,8 @@ fn spawn_planet(
let pcollide = ( let pcollide = (
shape, shape,
Friction { Friction {
static_coefficient: 0.8, static_coefficient: 1.2,
dynamic_coefficient: 0.5, ..Default::default()
combine_rule: CoefficientCombine::Average,
}, },
Restitution::new(0.8), Restitution::new(0.8),
); );
@ -42,7 +41,7 @@ fn spawn_planet(
RigidBody::Static, RigidBody::Static,
pcollide, pcollide,
CyberPlanet, CyberPlanet,
CollisionLayers::new(LayerMask::ALL, LayerMask::ALL), CollisionLayers::new(u32::MAX, u32::MAX),
CollisionMargin(0.2), CollisionMargin(0.2),
)); ));
} }
@ -137,6 +136,7 @@ fn gen_planet(radius: f32) -> (Mesh, Collider) {
} }
} }
dbg!(&colors.len());
mesh.insert_attribute(Mesh::ATTRIBUTE_COLOR, colors); mesh.insert_attribute(Mesh::ATTRIBUTE_COLOR, colors);
(mesh, collider) (mesh, collider)

View file

@ -1,11 +1,10 @@
use avian3d::prelude::LinearVelocity; use avian3d::prelude::LinearVelocity;
use bevy::{ use bevy::{
app::Update, app::{Startup, Update},
prelude::{ prelude::{
AlignSelf, App, AssetServer, Color, Commands, Component, Entity, Plugin, Query, Res, Style, AlignSelf, App, AssetServer, Color, Commands, Component, Plugin, Query, Res, Style, Text,
Text, TextBundle, TextSection, TextStyle, With, TextBundle, TextSection, TextStyle, Transform, With,
}, },
ui::TargetCamera,
}; };
use crate::bike::CyberBikeBody; use crate::bike::CyberBikeBody;
@ -13,11 +12,7 @@ use crate::bike::CyberBikeBody;
#[derive(Component)] #[derive(Component)]
struct UpText; struct UpText;
pub(crate) fn setup_ui( fn setup_ui(mut commands: Commands, asset_server: Res<AssetServer>) {
mut commands: Commands,
asset_server: Res<AssetServer>,
target_camera: Entity,
) {
commands commands
.spawn(TextBundle { .spawn(TextBundle {
style: Style { style: Style {
@ -39,16 +34,16 @@ pub(crate) fn setup_ui(
}, },
..Default::default() ..Default::default()
}) })
.insert((UpText, TargetCamera(target_camera))); .insert(UpText);
} }
fn update_ui( fn update_ui(
state_query: Query<&LinearVelocity, With<CyberBikeBody>>, state_query: Query<(&LinearVelocity, &Transform), With<CyberBikeBody>>,
mut text_query: Query<&mut Text, With<UpText>>, mut text_query: Query<&mut Text, With<UpText>>,
) { ) {
let mut text = text_query.single_mut(); let mut text = text_query.single_mut();
let velocity = state_query.single(); let (velocity, xform) = state_query.single();
let speed = velocity.0.length(); let speed = velocity.dot(*xform.forward());
text.sections[0].value = format!("spd: {:.2}", speed); text.sections[0].value = format!("spd: {:.2}", speed);
} }
@ -56,6 +51,7 @@ pub struct CyberUIPlugin;
impl Plugin for CyberUIPlugin { impl Plugin for CyberUIPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_systems(Update, update_ui); app.add_systems(Startup, setup_ui)
.add_systems(Update, update_ui);
} }
} }