Compare commits

...

10 commits

Author SHA1 Message Date
Joe Ardent
ff9b1671b1 checkpoint; wheel_forces skeleton fn 2024-06-15 15:43:48 -07:00
Joe Ardent
59f0ed89e9 checkpoint, frictionless wheels 2024-06-15 14:18:17 -07:00
Joe Ardent
3b307f4389 better, wheels still get mired 2024-06-13 16:29:50 -07:00
Joe Ardent
66724e8946 still not showing the color/textures 2024-06-13 14:50:41 -07:00
Joe Ardent
63ed962155 update to bevy 13 2024-06-13 13:00:39 -07:00
Joe Ardent
82f95cf070 silence lints 2023-05-10 11:21:30 -07:00
Joe Ardent
3e5e51401a misc conf changes and stuffs 2023-05-09 13:34:54 -07:00
Joe Ardent
c676c93637 clean up wheel stuff 2023-05-08 21:52:45 -07:00
Joe Ardent
0745760164 use torus for wheels 2023-05-08 21:18:26 -07:00
Joe Ardent
1dee14148d update hexasphere 2023-03-31 21:51:11 -07:00
17 changed files with 2570 additions and 1520 deletions

3140
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -7,15 +7,15 @@ edition = "2021"
rand = "0.8" rand = "0.8"
# bevy_polyline = "0.4" # bevy_polyline = "0.4"
noise = { git = "https://github.com/Razaekel/noise-rs" } noise = { git = "https://github.com/Razaekel/noise-rs" }
hexasphere = "7" hexasphere = "8"
wgpu = "0.15" wgpu = "0.19"
bevy-inspector-egui = "0.18" bevy-inspector-egui = "0.24"
[features] [features]
inspector = [] inspector = []
[dependencies.bevy] [dependencies.bevy]
version = "0.10" version = "0.13"
default-features = false default-features = false
features = [ features = [
"bevy_gilrs", "bevy_gilrs",
@ -27,11 +27,12 @@ features = [
"bevy_text", "bevy_text",
"bevy_gltf", "bevy_gltf",
"bevy_sprite", "bevy_sprite",
"tonemapping_luts"
] ]
[dependencies.bevy_rapier3d] [dependencies.bevy_rapier3d]
features = ["debug-render-3d"] features = ["debug-render-3d"]
version = "0.21" version = "0.26"
# Maybe also enable only a small amount of optimization for our code: # Maybe also enable only a small amount of optimization for our code:
[profile.dev] [profile.dev]

View file

@ -4,6 +4,7 @@ use bevy::{
prelude::{Component, ReflectResource, Resource, Vec3}, prelude::{Component, ReflectResource, Resource, Vec3},
reflect::Reflect, reflect::Reflect,
}; };
use bevy_rapier3d::dynamics::Velocity;
#[derive(Debug, Resource)] #[derive(Debug, Resource)]
pub(crate) struct ActionDebugInstant(pub Instant); pub(crate) struct ActionDebugInstant(pub Instant);
@ -26,20 +27,8 @@ impl ActionDebugInstant {
} }
} }
#[derive(Debug, Component)] #[derive(Debug, Component, Default)]
pub(super) struct Tunneling { pub struct PreviousVelocity(pub Velocity);
pub frames: usize,
pub dir: Vec3,
}
impl Default for Tunneling {
fn default() -> Self {
Tunneling {
frames: 15,
dir: Vec3::ZERO,
}
}
}
#[derive(Debug, Resource, Reflect)] #[derive(Debug, Resource, Reflect)]
#[reflect(Resource)] #[reflect(Resource)]

View file

@ -1,7 +1,7 @@
use bevy::{ use bevy::{
diagnostic::FrameTimeDiagnosticsPlugin, diagnostic::FrameTimeDiagnosticsPlugin,
ecs::reflect::ReflectResource, ecs::reflect::ReflectResource,
prelude::{App, IntoSystemConfigs, Plugin, Resource}, prelude::{App, IntoSystemConfigs, Plugin, Resource, Startup, Update},
reflect::Reflect, reflect::Reflect,
}; };
use bevy_rapier3d::prelude::{NoUserData, RapierPhysicsPlugin}; use bevy_rapier3d::prelude::{NoUserData, RapierPhysicsPlugin};
@ -28,18 +28,18 @@ impl Plugin for CyberActionPlugin {
.init_resource::<CyberLean>() .init_resource::<CyberLean>()
.register_type::<CyberLean>() .register_type::<CyberLean>()
.register_type::<CatControllerSettings>() .register_type::<CatControllerSettings>()
.add_plugin(RapierPhysicsPlugin::<NoUserData>::default()) .add_plugins(RapierPhysicsPlugin::<NoUserData>::default())
.add_startup_system(timestep_setup) .add_systems(Startup, physics_init)
.add_plugin(FrameTimeDiagnosticsPlugin::default()) .add_plugins(FrameTimeDiagnosticsPlugin)
.add_systems( .add_systems(
Update,
( (
gravity, reset_body_force,
reset_wheel_forces,
cyber_lean, cyber_lean,
falling_cat, falling_cat,
input_forces, input_forces,
drag, drag,
tunnel_out,
surface_fix,
) )
.chain(), .chain(),
); );

View file

@ -1,21 +1,217 @@
use std::f32::consts::{FRAC_PI_3, FRAC_PI_4}; use std::f32::consts::{FRAC_PI_3, FRAC_PI_4};
use bevy::prelude::{ use bevy::prelude::{
Commands, Entity, Quat, Query, Res, ResMut, Time, Transform, Vec3, With, Without, Commands, Entity, Local, Quat, Query, Res, ResMut, Time, Transform, Vec3, With, Without,
}; };
use bevy_rapier3d::prelude::{ use bevy_rapier3d::{
CollisionGroups, ExternalForce, Group, MultibodyJoint, QueryFilter, RapierConfiguration, dynamics::MassProperties,
RapierContext, ReadMassProperties, TimestepMode, Velocity, prelude::{
CollisionGroups, ExternalForce, Group, MultibodyJoint, QueryFilter, RapierConfiguration,
RapierContext, ReadMassProperties, TimestepMode, Velocity,
},
}; };
#[cfg(feature = "inspector")] #[cfg(feature = "inspector")]
use super::ActionDebugInstant; use super::ActionDebugInstant;
use super::{CatControllerSettings, CatControllerState, CyberLean, MovementSettings, Tunneling}; use super::{
CatControllerSettings, CatControllerState, CyberLean, MovementSettings, PreviousVelocity,
};
use crate::{ use crate::{
bike::{CyberBikeBody, CyberSteering, CyberWheel, WheelConfig, BIKE_WHEEL_COLLISION_GROUP}, bike::{CyberBikeBody, CyberSteering, CyberWheel, WheelConfig, BIKE_WHEEL_COLLISION_GROUP},
input::InputState, input::InputState,
}; };
pub(super) fn physics_init(
mut config: ResMut<RapierConfiguration>,
settings: Res<MovementSettings>,
) {
config.timestep_mode = TimestepMode::Variable {
max_dt: 1.0 / 60.0,
time_scale: 1.0,
substeps: 3,
};
config.gravity = Vec3::NEG_Y * settings.gravity;
}
pub(super) fn reset_body_force(
mut query: Query<&mut ExternalForce, With<CyberBikeBody>>,
#[cfg(feature = "inspector")] mut debug_instant: ResMut<ActionDebugInstant>,
) {
let mut body_force = query.single_mut();
#[cfg(feature = "inspector")]
{
if debug_instant.elapsed().as_millis() > 1000 {
//dbg!(&body_force);
debug_instant.reset();
}
}
body_force.force = Vec3::ZERO;
body_force.torque = Vec3::ZERO;
}
pub(super) fn reset_wheel_forces(mut query: Query<&mut ExternalForce, With<CyberWheel>>) {
for mut force in query.iter_mut() {
force.force = Vec3::ZERO;
force.torque = Vec3::ZERO;
}
}
/// The desired lean angle, given steering input and speed.
pub(super) fn cyber_lean(
bike_state: Query<(&Velocity, &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 v_squared = vel.powi(2);
let steering_angle = yaw_to_angle(input.yaw);
let wheels: Vec<_> = wheels.iter().map(|w| w.translation).collect();
let wheel_base = (wheels[0] - wheels[1]).length();
let radius = wheel_base / steering_angle.tan();
let gravity = gravity_settings.gravity;
let v2_r = v_squared / radius;
let tan_theta = (v2_r / gravity).clamp(-FRAC_PI_3, FRAC_PI_3);
if tan_theta.is_finite() && !tan_theta.is_subnormal() {
lean.lean = tan_theta.atan().clamp(-FRAC_PI_4, FRAC_PI_4);
} else {
lean.lean = 0.0;
}
}
/// PID-based controller for stabilizing attitude; keeps the cyberbike upright.
pub(super) fn falling_cat(
mut bike_query: Query<(
&Transform,
&Velocity,
&mut ExternalForce,
&mut CatControllerState,
)>,
time: Res<Time>,
settings: Res<CatControllerSettings>,
lean: Res<CyberLean>,
mut count: Local<usize>,
) {
let (xform, vel, mut forces, mut control_vars) = bike_query.single_mut();
let world_up = Vec3::Y; //xform.translation.normalize();
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());
if *count < 30 {
if *count == 0 {
//dbg!(&xform.translation);
//dbg!(&vel.linvel);
}
*count += 1;
} else {
*count = 0;
}
// only try to correct roll if we're not totally vertical
if pitch_error.abs() < 0.95 {
let (derivative, integral) = control_vars.update_roll(roll_error, time.delta_seconds());
let mag =
(settings.kp * roll_error) + (settings.ki * integral) + (settings.kd * derivative);
if mag.is_finite() {
forces.torque += xform.back() * mag;
}
}
}
pub(super) fn wheel_forces(
mut wheels_query: Query<
(
&Transform,
&Velocity,
&mut PreviousVelocity,
Option<&CyberSteering>,
),
With<CyberWheel>,
>,
mut body_query: Query<
(
&Transform,
&Velocity,
&mut PreviousVelocity,
&ReadMassProperties,
),
With<CyberBikeBody>,
>,
wheel_config: Res<WheelConfig>,
rapier_config: Res<RapierConfiguration>,
context: Res<RapierContext>,
settings: Res<MovementSettings>,
input: Res<InputState>,
) {
let (body_xform, body_vel, mut body_pvel, body_mass) = body_query.single_mut();
let gvec = rapier_config.gravity;
// body kinematics
let body_lin_accel = body_vel.linvel - body_pvel.0.linvel;
let body_ang_accel = body_vel.angvel - body_pvel.0.angvel;
let mut body_forward = body_vel
.linvel
.try_normalize()
.unwrap_or_else(|| body_xform.forward().normalize());
// we're just projecting onto the ground, so nuke the Y.
body_forward.y = 0.0;
let body_half_mass = body_mass.mass * 0.5;
let g = gvec * settings.gravity * body_half_mass;
// inputs
let steering_angle = yaw_to_angle(input.yaw);
let throttle = input.throttle * settings.accel;
for (xform, vel, mut pvel, steering) in wheels_query.iter_mut() {
if let Some((_, projected)) =
context.project_point(xform.translation, true, QueryFilter::only_fixed())
{
let normal = -(projected.point - xform.translation);
let len = normal.length();
let normal = normal.normalize();
if len < wheel_config.radius {
// we're in contact
// do gravity's share
// do linear acceleration's share
// do friction's share
// do thrust's share
// do steering's share
}
}
}
body_pvel.0 = *body_vel;
}
/// 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);
}
}
// helpers
fn yaw_to_angle(yaw: f32) -> f32 { fn yaw_to_angle(yaw: f32) -> f32 {
let v = yaw.powi(5) * FRAC_PI_4; let v = yaw.powi(5) * FRAC_PI_4;
if v.is_normal() { if v.is_normal() {
@ -35,211 +231,3 @@ fn rotate_point(pt: &Vec3, rot: &Quat) -> Vec3 {
// why does this need to be inverted??? // why does this need to be inverted???
-Vec3::from_array([rot_qpt.x, rot_qpt.y, rot_qpt.z]) -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>,
#[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;
}
/// The desired lean angle, given steering input and speed.
pub(super) fn cyber_lean(
bike_state: Query<(&Velocity, &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 v_squared = vel.powi(2);
let steering_angle = yaw_to_angle(input.yaw);
let wheels: Vec<_> = wheels.iter().map(|w| w.translation).collect();
let wheel_base = (wheels[0] - wheels[1]).length();
let radius = wheel_base / steering_angle.tan();
let gravity = gravity_settings.gravity;
let v2_r = v_squared / radius;
let tan_theta = (v2_r / gravity).clamp(-FRAC_PI_3, FRAC_PI_3);
if tan_theta.is_finite() && !tan_theta.is_subnormal() {
lean.lean = tan_theta.atan().clamp(-FRAC_PI_4, FRAC_PI_4);
} else {
lean.lean = 0.0;
}
}
/// PID-based controller for stabilizing attitude; keeps the cyberbike upright.
pub(super) fn falling_cat(
mut bike_query: Query<(&Transform, &mut ExternalForce, &mut CatControllerState)>,
time: Res<Time>,
settings: Res<CatControllerSettings>,
lean: Res<CyberLean>,
) {
let (xform, mut forces, mut control_vars) = bike_query.single_mut();
let world_up = xform.translation.normalize();
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());
// only try to correct roll if we're not totally vertical
if pitch_error.abs() < 0.95 {
let (derivative, integral) = control_vars.update_roll(roll_error, time.delta_seconds());
let mag =
(settings.kp * roll_error) + (settings.ki * integral) + (settings.kd * derivative);
if mag.is_finite() {
forces.torque += xform.back() * mag;
}
}
}
/// Apply forces to the cyberbike as a result of input.
pub(super) fn input_forces(
settings: Res<MovementSettings>,
input: Res<InputState>,
mut braking_query: Query<&mut MultibodyJoint, (Without<CyberSteering>, With<CyberWheel>)>,
mut body_query: Query<
(&Transform, &mut ExternalForce),
(With<CyberBikeBody>, Without<CyberSteering>),
>,
mut steering_query: Query<&mut MultibodyJoint, With<CyberSteering>>,
) {
let (xform, mut forces) = body_query.single_mut();
// 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;
// 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 };
motor.data = (*motor
.data
.as_revolute_mut()
.unwrap()
.set_motor_max_force(factor)
.set_motor_velocity(speed, factor))
.into();
}
// 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);
}
}

View file

@ -7,7 +7,7 @@ use bevy_rapier3d::prelude::{
ReadMassProperties, Restitution, RigidBody, Sleeping, TransformInterpolation, Velocity, ReadMassProperties, Restitution, RigidBody, Sleeping, TransformInterpolation, Velocity,
}; };
use super::{spawn_tires, CyberBikeBody, Meshterial, WheelConfig, BIKE_BODY_COLLISION_GROUP}; use super::{spawn_wheels, CyberBikeBody, Meshterial, WheelConfig, BIKE_BODY_COLLISION_GROUP};
use crate::{action::CatControllerState, planet::PLANET_RADIUS}; use crate::{action::CatControllerState, planet::PLANET_RADIUS};
pub(super) fn spawn_cyberbike( pub(super) fn spawn_cyberbike(
@ -16,13 +16,12 @@ pub(super) fn spawn_cyberbike(
wheel_conf: Res<WheelConfig>, wheel_conf: Res<WheelConfig>,
mut meshterials: Meshterial, mut meshterials: Meshterial,
) { ) {
let altitude = PLANET_RADIUS - 10.0; let altitude = 3.0;
let mut xform = Transform::from_translation(Vec3::X * altitude) let mut xform = Transform::from_translation(Vec3::Y * altitude);
.with_rotation(Quat::from_axis_angle(Vec3::Z, -89.0f32.to_radians()));
let right = xform.right() * 350.0; // let right = xform.right() * 350.0;
xform.translation += right; // xform.translation += right;
let damping = Damping { let damping = Damping {
angular_damping: 2.0, angular_damping: 2.0,
@ -39,7 +38,7 @@ pub(super) fn spawn_cyberbike(
..Default::default() ..Default::default()
}; };
let mass_properties = ColliderMassProperties::Density(0.9); let mass_properties = ColliderMassProperties::Density(1.5);
let (membership, filter) = BIKE_BODY_COLLISION_GROUP; let (membership, filter) = BIKE_BODY_COLLISION_GROUP;
let bike_collision_group = CollisionGroups::new(membership, filter); let bike_collision_group = CollisionGroups::new(membership, filter);
@ -81,5 +80,5 @@ pub(super) fn spawn_cyberbike(
.insert(CatControllerState::default()) .insert(CatControllerState::default())
.id(); .id();
spawn_tires(&mut commands, bike, &wheel_conf, &mut meshterials); spawn_wheels(&mut commands, bike, &wheel_conf, &mut meshterials);
} }

View file

@ -22,7 +22,6 @@ pub struct WheelConfig {
pub stiffness: f32, pub stiffness: f32,
pub damping: f32, pub damping: f32,
pub radius: f32, pub radius: f32,
pub thickness: f32,
pub friction: f32, pub friction: f32,
pub restitution: f32, pub restitution: f32,
pub density: f32, pub density: f32,
@ -32,16 +31,15 @@ impl Default for WheelConfig {
fn default() -> Self { fn default() -> Self {
Self { Self {
front_forward: 0.8, front_forward: 0.8,
rear_back: 1.1, rear_back: 1.0,
y: -0.1, y: -0.1,
limits: [-0.5, 0.1], limits: [-0.5, 0.1],
stiffness: 50.0, stiffness: 30.0,
damping: 8.0, damping: 8.0,
radius: 0.38, radius: 0.3,
thickness: 0.2, friction: 0.0,
friction: 1.2,
restitution: 0.95, restitution: 0.95,
density: 0.9, density: 0.05,
} }
} }
} }

View file

@ -1,17 +1,16 @@
mod body; mod body;
mod components; mod components;
//mod controller;
mod wheels; mod wheels;
use bevy::prelude::{ use bevy::prelude::{App, Assets, Mesh, Plugin, PostStartup, ResMut, StandardMaterial};
App, Assets, IntoSystemConfig, Mesh, Plugin, ResMut, StandardMaterial, StartupSet,
};
use bevy_rapier3d::prelude::Group; use bevy_rapier3d::prelude::Group;
pub(crate) use self::components::*; pub(crate) use self::components::*;
use self::{body::spawn_cyberbike, wheels::spawn_tires}; use self::{body::spawn_cyberbike, wheels::spawn_wheels};
pub const BIKE_BODY_COLLISION_GROUP: (Group, Group) = (Group::GROUP_1, Group::GROUP_1); pub const BIKE_BODY_COLLISION_GROUP: (Group, Group) = (Group::GROUP_1, Group::GROUP_1);
pub const BIKE_WHEEL_COLLISION_GROUP: (Group, Group) = (Group::GROUP_10, Group::GROUP_10); pub const BIKE_WHEEL_COLLISION_GROUP: (Group, Group) = (Group::GROUP_2, Group::GROUP_2);
type Meshterial<'a> = ( type Meshterial<'a> = (
ResMut<'a, Assets<Mesh>>, ResMut<'a, Assets<Mesh>>,
@ -23,6 +22,6 @@ impl Plugin for CyberBikePlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.insert_resource(WheelConfig::default()) app.insert_resource(WheelConfig::default())
.register_type::<WheelConfig>() .register_type::<WheelConfig>()
.add_startup_system(spawn_cyberbike.in_base_set(StartupSet::PostStartup)); .add_systems(PostStartup, spawn_cyberbike);
} }
} }

View file

@ -1,13 +1,17 @@
use bevy::prelude::{shape::UVSphere as Tire, *}; use bevy::prelude::*;
use bevy_rapier3d::prelude::{ use bevy_rapier3d::{
Ccd, CoefficientCombineRule, Collider, ColliderMassProperties, CollisionGroups, Damping, dynamics::{FixedJointBuilder, Velocity},
ExternalForce, Friction, MultibodyJoint, PrismaticJointBuilder, Restitution, prelude::{
RevoluteJointBuilder, RigidBody, Sleeping, TransformInterpolation, Ccd, CoefficientCombineRule, Collider, ColliderMassProperties, CollisionGroups, Damping,
ExternalForce, Friction, MultibodyJoint, PrismaticJointBuilder, Restitution,
RevoluteJointBuilder, RigidBody, Sleeping, TransformInterpolation,
},
}; };
use super::{CyberSteering, CyberWheel, Meshterial, WheelConfig, BIKE_WHEEL_COLLISION_GROUP}; use super::{CyberSteering, CyberWheel, Meshterial, WheelConfig, BIKE_WHEEL_COLLISION_GROUP};
use crate::action::PreviousVelocity;
pub fn spawn_tires( pub(crate) fn spawn_wheels(
commands: &mut Commands, commands: &mut Commands,
bike: Entity, bike: Entity,
conf: &WheelConfig, conf: &WheelConfig,
@ -16,8 +20,6 @@ pub fn spawn_tires(
let (membership, filter) = BIKE_WHEEL_COLLISION_GROUP; let (membership, filter) = BIKE_WHEEL_COLLISION_GROUP;
let wheels_collision_group = CollisionGroups::new(membership, filter); let wheels_collision_group = CollisionGroups::new(membership, filter);
let wheel_y = conf.y; let wheel_y = conf.y;
let wheel_rad = conf.radius;
let _tire_thickness = conf.thickness;
let stiffness = conf.stiffness; let stiffness = conf.stiffness;
let not_sleeping = Sleeping::disabled(); let not_sleeping = Sleeping::disabled();
let ccd = Ccd { enabled: true }; let ccd = Ccd { enabled: true };
@ -25,32 +27,9 @@ pub fn spawn_tires(
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 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 friction = Friction { let friction = Friction {
coefficient: conf.friction, coefficient: conf.friction,
combine_rule: CoefficientCombineRule::Average, combine_rule: CoefficientCombineRule::Min,
}; };
let mut wheel_poses = Vec::with_capacity(2); let mut wheel_poses = Vec::with_capacity(2);
@ -72,35 +51,43 @@ pub fn spawn_tires(
} }
for (offset, steering) in wheel_poses { for (offset, steering) in wheel_poses {
let wheel_damping = Damping { let (mesh, collider) = gen_tires(conf);
linear_damping: 0.8,
let material = StandardMaterial {
base_color: Color::YELLOW,
alpha_mode: AlphaMode::Opaque,
perceptual_roughness: 0.1,
..Default::default() ..Default::default()
}; };
let wheel_collider = Collider::ball(wheel_rad); let pbr_bundle = PbrBundle {
let mass_props = ColliderMassProperties::Density(conf.density); material: materials.add(material),
let damping = conf.damping; mesh: meshes.add(mesh),
..Default::default()
};
let prismatic_axis = if steering.is_some() { let mass_props = ColliderMassProperties::Density(conf.density);
let suspension_damping = conf.damping;
let suspension_axis = if steering.is_some() {
rake_vec rake_vec
} else { } else {
Vec3::Y Vec3::Y
}; };
let prismatic_builder = PrismaticJointBuilder::new(prismatic_axis) let suspension_joint_builder = PrismaticJointBuilder::new(suspension_axis)
.local_anchor1(offset) .local_anchor1(offset)
.limits(limits) .limits(limits)
.motor_position(limits[0], stiffness, damping); .motor_position(limits[0], stiffness, suspension_damping);
let prismatic_joint = MultibodyJoint::new(bike, prismatic_builder); let suspension_joint = MultibodyJoint::new(bike, suspension_joint_builder);
let fork_rb_entity = commands let fork_rb_entity = commands
.spawn(RigidBody::Dynamic) .spawn(RigidBody::Dynamic)
.insert(prismatic_joint) .insert(suspension_joint)
.insert(not_sleeping) .insert(not_sleeping)
.id(); .id();
let axel_parent_entity = if let Some(steering) = steering { let axel_parent_entity = if let Some(steering) = steering {
let neck_builder = let neck_builder = FixedJointBuilder::new().local_anchor1(Vec3::new(0.0, 0.0, 0.1)); // this adds another 0.1m of trail
RevoluteJointBuilder::new(rake_vec).local_anchor1(Vec3::new(0.0, -0.08, 0.1)); // this adds another 0.1m of trail
let neck_joint = MultibodyJoint::new(fork_rb_entity, neck_builder); let neck_joint = MultibodyJoint::new(fork_rb_entity, neck_builder);
let neck = commands let neck = commands
.spawn(RigidBody::Dynamic) .spawn(RigidBody::Dynamic)
@ -113,29 +100,35 @@ pub fn spawn_tires(
fork_rb_entity fork_rb_entity
}; };
let axel_builder = RevoluteJointBuilder::new(Vec3::X); let axel_builder = FixedJointBuilder::new();
let axel_joint = MultibodyJoint::new(axel_parent_entity, axel_builder); let axel_joint = MultibodyJoint::new(axel_parent_entity, axel_builder);
// re-orient the joint so that the wheel is correctly oriented
//let real_axel = *axel_joint.data.set_local_axis1(Vec3::X);
//axel_joint.data = real_axel;
commands commands.spawn(pbr_bundle).insert((
.spawn(pbr_bundle.clone()) collider,
.insert(( mass_props,
wheel_collider, //wheel_damping,
mass_props, ccd,
wheel_damping, not_sleeping,
ccd, axel_joint,
not_sleeping, wheels_collision_group,
axel_joint, friction,
wheels_collision_group, CyberWheel,
friction, Velocity::default(),
CyberWheel, PreviousVelocity::default(),
ExternalForce::default(), ExternalForce::default(),
Restitution::new(conf.restitution), SpatialBundle::default(),
TransformInterpolation::default(), TransformInterpolation::default(),
RigidBody::Dynamic, RigidBody::Dynamic,
)) ));
.insert(SpatialBundle::default());
} }
} }
// do mesh shit
fn gen_tires(conf: &WheelConfig) -> (Mesh, Collider) {
let wheel_rad = conf.radius;
let tire = Sphere::new(wheel_rad);
let mesh = Mesh::from(tire);
let wheel_collider = Collider::ball(wheel_rad);
(mesh, wheel_collider)
}

View file

@ -67,7 +67,7 @@ fn follow_cyberbike(
offset: Res<DebugCamOffset>, offset: Res<DebugCamOffset>,
) { ) {
let bike_xform = *query.p0().single(); let bike_xform = *query.p0().single();
let up = bike_xform.translation.normalize(); let up = Vec3::Y; //bike_xform.translation.normalize();
for (mut cam_xform, cam_type) in query.p1().iter_mut() { for (mut cam_xform, cam_type) in query.p1().iter_mut() {
match *cam_type { match *cam_type {
@ -81,7 +81,7 @@ fn follow_cyberbike(
// handle input pitch // handle input pitch
let angle = input.pitch.powi(3) * MAX_PITCH; let angle = input.pitch.powi(3) * MAX_PITCH;
let axis = cam_xform.right(); let axis = cam_xform.right();
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 = bike_xform.to_owned(); let mut ncx = bike_xform.to_owned();
@ -101,7 +101,7 @@ fn update_active_camera(
) { ) {
// find the camera with the current state, set it as the ActiveCamera // find the camera with the current state, set it as the ActiveCamera
query.iter_mut().for_each(|(mut cam, cyber)| { query.iter_mut().for_each(|(mut cam, cyber)| {
if cyber.eq(&state.0) { if cyber.eq(&state.get()) {
cam.is_active = true; cam.is_active = true;
} else { } else {
cam.is_active = false; cam.is_active = false;
@ -112,13 +112,13 @@ fn update_active_camera(
fn cycle_cam_state( fn cycle_cam_state(
state: Res<State<CyberCameras>>, state: Res<State<CyberCameras>>,
mut next: ResMut<NextState<CyberCameras>>, mut next: ResMut<NextState<CyberCameras>>,
mut keys: ResMut<Input<KeyCode>>, mut keys: ResMut<ButtonInput<KeyCode>>,
) { ) {
if keys.just_pressed(KeyCode::D) { if keys.just_pressed(KeyCode::KeyD) {
let new_state = state.0.next(); let new_state = state.get().next();
info!("{:?}", new_state); info!("{:?}", new_state);
next.set(new_state); next.set(new_state);
keys.reset(KeyCode::D); keys.reset(KeyCode::KeyD);
} }
} }
@ -132,9 +132,10 @@ impl Plugin for CyberCamPlugin {
fn common(app: &mut bevy::prelude::App) { fn common(app: &mut bevy::prelude::App) {
app.insert_resource(DebugCamOffset::default()) app.insert_resource(DebugCamOffset::default())
.add_startup_system(setup_cybercams) .add_systems(Startup, setup_cybercams)
.add_state::<CyberCameras>() .init_state::<CyberCameras>()
.add_system(cycle_cam_state) .add_systems(
.add_system(update_active_camera) Update,
.add_system(follow_cyberbike); (cycle_cam_state, update_active_camera, follow_cyberbike),
);
} }

View file

@ -19,16 +19,17 @@ impl Plugin for CyberGlamorPlugin {
let mode = DebugRenderMode::CONTACTS let mode = DebugRenderMode::CONTACTS
| DebugRenderMode::SOLVER_CONTACTS | DebugRenderMode::SOLVER_CONTACTS
| DebugRenderMode::JOINTS | DebugRenderMode::JOINTS
| DebugRenderMode::COLLIDER_SHAPES
| DebugRenderMode::RIGID_BODY_AXES; | DebugRenderMode::RIGID_BODY_AXES;
let rplugin = RapierDebugRenderPlugin { let rplugin = RapierDebugRenderPlugin {
style, style,
always_on_top: true, // always_on_top: true,
enabled: true, enabled: true,
mode, mode,
}; };
app.add_plugin(rplugin); app.add_plugins(rplugin);
} }
} }
} }

View file

@ -14,22 +14,22 @@ pub(crate) struct InputState {
pub pitch: f32, 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 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 { for key in keyset {
match key { match key {
KeyCode::Left => offset.rot -= 5.0, KeyCode::ArrowLeft => offset.rot -= 5.0,
KeyCode::Right => offset.rot += 5.0, KeyCode::ArrowRight => offset.rot += 5.0,
KeyCode::Up => { KeyCode::ArrowUp => {
if shifted { if shifted {
offset.alt += 0.5; offset.alt += 0.5;
} else { } else {
offset.dist -= 0.5; offset.dist -= 0.5;
} }
} }
KeyCode::Down => { KeyCode::ArrowDown => {
if shifted { if shifted {
offset.alt -= 0.5; offset.alt -= 0.5;
} else { } 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 { 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(); keys.reset_all();
if shifted && !unpressed { if shifted && !unpressed {
keys.press(KeyCode::LShift); keys.press(KeyCode::ShiftLeft);
} }
} }
} }
fn update_input(mut events: EventReader<GamepadEvent>, mut istate: ResMut<InputState>) { 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 { match pad_event {
GamepadEvent::Button(button_event) => { GamepadEvent::Button(button_event) => {
let GamepadButtonChangedEvent { let GamepadButtonChangedEvent {
@ -92,7 +93,6 @@ pub struct CyberInputPlugin;
impl Plugin for CyberInputPlugin { impl Plugin for CyberInputPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.init_resource::<InputState>() app.init_resource::<InputState>()
.add_system(update_input) .add_systems(Update, (update_input, update_debug_cam));
.add_system(update_debug_cam);
} }
} }

View file

@ -1,5 +1,4 @@
use bevy::{ use bevy::{
ecs::schedule::SystemSet,
prelude::{Query, Vec3, Window, With}, prelude::{Query, Vec3, Window, With},
window::PrimaryWindow, window::PrimaryWindow,
}; };
@ -13,14 +12,6 @@ pub mod lights;
pub mod planet; pub mod planet;
pub mod ui; pub mod ui;
#[derive(Clone, Debug, Hash, PartialEq, Eq, SystemSet)]
pub enum Label {
Geometry,
Glamor,
Input,
Action,
}
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 = 90.0; pub const LIGHT_RANGE: f32 = 900.0;
fn spawn_static_lights( fn spawn_static_lights(
mut commands: Commands, mut commands: Commands,
@ -10,88 +10,37 @@ fn spawn_static_lights(
mut materials: ResMut<Assets<StandardMaterial>>, mut materials: ResMut<Assets<StandardMaterial>>,
) { ) {
let pink_light = PointLight { let pink_light = PointLight {
intensity: 1_00.0, intensity: 10_00.0,
range: LIGHT_RANGE, range: LIGHT_RANGE,
color: Color::PINK, color: Color::WHITE,
radius: 1.0, radius: 10.0,
shadows_enabled: true,
..Default::default()
};
let blue_light = PointLight {
intensity: 1_000.0,
range: LIGHT_RANGE,
color: Color::BLUE,
radius: 1.0,
shadows_enabled: true, shadows_enabled: true,
..Default::default() ..Default::default()
}; };
commands.insert_resource(AmbientLight { commands.insert_resource(AmbientLight {
color: Color::WHITE, color: Color::WHITE,
brightness: 0.2, brightness: 100.0,
}); });
let _cascade_shadow_config = CascadeShadowConfigBuilder { // let _cascade_shadow_config = CascadeShadowConfigBuilder {
first_cascade_far_bound: 0.3, // first_cascade_far_bound: 0.3,
maximum_distance: 3.0, // maximum_distance: 3.0,
..default() // ..default()
} // }
.build(); // .build();
// up light // up light
commands commands.spawn(PointLightBundle {
.spawn(PointLightBundle { transform: Transform::from_xyz(0.0, 100.0, 0.0),
transform: Transform::from_xyz(0.0, PLANET_RADIUS + 30.0, 0.0), point_light: pink_light,
point_light: pink_light, ..Default::default()
..Default::default() });
})
.with_children(|builder| {
builder.spawn(PbrBundle {
mesh: meshes.add(
Mesh::try_from(shape::Icosphere {
radius: 10.0,
subdivisions: 2,
})
.unwrap(),
),
material: materials.add(StandardMaterial {
base_color: Color::BLUE,
emissive: Color::PINK,
..Default::default()
}),
..Default::default()
});
});
// down light
commands
.spawn(PointLightBundle {
transform: Transform::from_xyz(0.0, -PLANET_RADIUS - 30.0, 0.0),
point_light: blue_light,
..Default::default()
})
.with_children(|builder| {
builder.spawn(PbrBundle {
mesh: meshes.add(
Mesh::try_from(shape::Icosphere {
radius: 10.0,
subdivisions: 2,
})
.unwrap(),
),
material: materials.add(StandardMaterial {
base_color: Color::PINK,
emissive: Color::BLUE,
..Default::default()
}),
..Default::default()
});
});
} }
pub struct CyberSpaceLightsPlugin; pub struct CyberSpaceLightsPlugin;
impl Plugin for CyberSpaceLightsPlugin { impl Plugin for CyberSpaceLightsPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_startup_system(spawn_static_lights); app.add_systems(Startup, spawn_static_lights);
} }
} }

View file

@ -21,18 +21,20 @@ fn main() {
}), }),
..Default::default() ..Default::default()
})) }))
.add_plugin(CyberPlanetPlugin) .add_plugins((
.add_plugin(CyberInputPlugin) CyberPlanetPlugin,
.add_plugin(CyberActionPlugin) CyberInputPlugin,
.add_plugin(CyberCamPlugin) CyberActionPlugin,
.add_plugin(CyberSpaceLightsPlugin) CyberCamPlugin,
.add_plugin(CyberUIPlugin) CyberSpaceLightsPlugin,
.add_plugin(CyberBikePlugin) CyberUIPlugin,
.add_startup_system(disable_mouse_trap) CyberBikePlugin,
.add_system(bevy::window::close_on_esc); ))
.add_systems(Startup, disable_mouse_trap)
.add_systems(Update, bevy::window::close_on_esc);
#[cfg(feature = "inspector")] #[cfg(feature = "inspector")]
app.add_plugin(CyberGlamorPlugin); app.add_plugins(CyberGlamorPlugin);
app.run(); app.run();
} }

View file

@ -1,18 +1,8 @@
//use core::slice::SlicePattern; use bevy::{prelude::*, render::color::Color};
use bevy::{
prelude::{shape::Icosphere, *},
render::{color::Color, mesh::Indices},
};
use bevy_rapier3d::prelude::*; use bevy_rapier3d::prelude::*;
use hexasphere::shapes::IcoSphere; // use noise::{HybridMulti, NoiseFn, SuperSimplex};
use noise::{HybridMulti, NoiseFn, SuperSimplex};
use rand::{Rng, SeedableRng};
use wgpu::PrimitiveTopology;
use crate::Label; pub const PLANET_RADIUS: f32 = 2_000.0;
pub const PLANET_RADIUS: f32 = 3_000.0;
pub const PLANET_HUE: f32 = 31.0; pub const PLANET_HUE: f32 = 31.0;
pub const PLANET_SATURATION: f32 = 1.0; pub const PLANET_SATURATION: f32 = 1.0;
@ -25,14 +15,14 @@ fn spawn_planet(
mut materials: ResMut<Assets<StandardMaterial>>, mut materials: ResMut<Assets<StandardMaterial>>,
) { ) {
//let color = Color::rgb(0.74, 0.5334, 0.176); //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 (mesh, shape) = gen_planet(2999.9);
let pbody = (RigidBody::Fixed, Ccd { enabled: true }); let pbody = (
RigidBody::Fixed,
Ccd { enabled: true },
Transform::from_xyz(0.0, 0.1, 0.0),
);
let pcollide = ( let pcollide = (
shape, shape,
@ -41,12 +31,19 @@ fn spawn_planet(
..Default::default() ..Default::default()
}, },
Restitution::new(0.8), Restitution::new(0.8),
Transform::from_xyz(0.0, 1.0, 0.0),
// same as the bike body
CollisionGroups::new(Group::GROUP_1, Group::GROUP_1),
); );
commands commands
.spawn(PbrBundle { .spawn(PbrBundle {
mesh: meshes.add(mesh), mesh: meshes.add(mesh),
material: materials.add(Color::WHITE.into()), material: materials.add(StandardMaterial {
base_color: Color::GREEN,
..Default::default()
}),
transform: Transform::from_xyz(0.0, 0.1, 0.0),
..Default::default() ..Default::default()
}) })
.insert(pbody) .insert(pbody)
@ -57,7 +54,7 @@ fn spawn_planet(
pub struct CyberPlanetPlugin; pub struct CyberPlanetPlugin;
impl Plugin for CyberPlanetPlugin { impl Plugin for CyberPlanetPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_startup_system(spawn_planet); app.add_systems(Startup, spawn_planet);
} }
} }
@ -65,105 +62,10 @@ impl Plugin for CyberPlanetPlugin {
// utils // utils
//--------------------------------------------------------------------- //---------------------------------------------------------------------
fn gen_planet(sphere: Icosphere) -> (Mesh, Collider) { fn gen_planet(span: f32) -> (Mesh, Collider) {
// straight-up stolen from Bevy's impl of Mesh from Icosphere, so I can do the let mut mesh = Cuboid::new(span, 1.0, span);
// displacement before normals are calculated.
let generated = IcoSphere::new(sphere.subdivisions, |point| {
let inclination = point.y.acos();
let azimuth = point.z.atan2(point.x);
let norm_inclination = inclination / std::f32::consts::PI; let collider = Collider::cuboid(span / 2.0, 0.5, span / 2.0);
let norm_azimuth = 0.5 - (azimuth / std::f32::consts::TAU);
[norm_azimuth, norm_inclination] (mesh.mesh(), collider)
});
let noise = HybridMulti::<SuperSimplex>::default();
let (mut min, mut max) = (f32::MAX, f32::MIN);
let noisy_points = generated
.raw_points()
.iter()
.map(|&p| {
let disp = noise.get(p.as_dvec3().into()) as f32 * 0.05;
let pt = p + (p.normalize() * disp);
pt.into()
})
.collect::<Vec<[f32; 3]>>();
let points = noisy_points
.iter()
.map(|&p| (Vec3::from_slice(&p) * sphere.radius).into())
.collect::<Vec<[f32; 3]>>();
for p in &points {
let v = Vec3::new(p[0], p[1], p[2]);
let v = v.length();
min = v.min(min);
max = v.max(max);
}
let uvs = generated.raw_data().to_owned();
let mut indices = Vec::with_capacity(generated.indices_per_main_triangle() * 20);
for i in 0..20 {
generated.get_indices(i, &mut indices);
}
let mut idxs = Vec::new();
for idx in indices.chunks_exact(3) {
idxs.push([idx[0], idx[1], idx[2]]);
}
let indices = Indices::U32(indices);
let shape = Collider::trimesh(points.iter().map(|p| Vect::from_slice(p)).collect(), idxs);
dbg!(&points.len());
let mut mesh = Mesh::new(PrimitiveTopology::TriangleList);
mesh.set_indices(Some(indices));
mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, points);
//mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs);
mesh.duplicate_vertices();
mesh.compute_flat_normals();
let tri_list = mesh
.attribute(Mesh::ATTRIBUTE_POSITION)
.unwrap()
.as_float3()
.unwrap();
dbg!(&tri_list.len());
let mut rng = rand::rngs::StdRng::seed_from_u64(57);
let mut colors = Vec::new();
for triangle in tri_list.chunks_exact(3) {
let mut lens = 0.0;
for &v in triangle {
let v = Vec3::new(v[0], v[1], v[2]);
lens += v.length();
}
let v = lens / 3.0;
//let l = val_norm(v, min - 500.0, max); //.powi(2);
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();
for _ in 0..3 {
colors.push(color.clone());
}
}
dbg!(&colors.len());
mesh.insert_attribute(Mesh::ATTRIBUTE_COLOR, colors);
(mesh, shape)
}
/// remaps v in low..high to 0..1
fn val_norm(v: f32, low: f32, high: f32) -> f32 {
// q = (p-A)*(D-C)/(B-A) + C, C = 0, D = 1
(v - low) / (high - low)
} }

View file

@ -1,9 +1,12 @@
use bevy::prelude::{ use bevy::{
AlignSelf, App, AssetServer, Color, Commands, Component, Plugin, Query, Res, Style, Text, app::{Startup, Update},
TextBundle, TextSection, TextStyle, Transform, With, prelude::{
AlignSelf, App, AssetServer, Color, Commands, Component, Plugin, Query, Res, Style, Text,
TextBundle, TextSection, TextStyle, Transform, With,
},
}; };
#[cfg(feature = "inspector")] //#[cfg(feature = "inspector")]
use bevy_inspector_egui::quick::WorldInspectorPlugin; //use bevy_inspector_egui::quick::WorldInspectorPlugin;
use bevy_rapier3d::prelude::Velocity; use bevy_rapier3d::prelude::Velocity;
use crate::bike::CyberBikeBody; use crate::bike::CyberBikeBody;
@ -42,7 +45,7 @@ fn update_ui(
) { ) {
let mut text = text_query.single_mut(); let mut text = text_query.single_mut();
let (velocity, xform) = state_query.single(); let (velocity, xform) = state_query.single();
let speed = velocity.linvel.dot(xform.forward()); let speed = velocity.linvel.dot(*xform.forward());
text.sections[0].value = format!("spd: {:.2}", speed); text.sections[0].value = format!("spd: {:.2}", speed);
} }
@ -51,8 +54,10 @@ pub struct CyberUIPlugin;
impl Plugin for CyberUIPlugin { impl Plugin for CyberUIPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
#[cfg(feature = "inspector")] #[cfg(feature = "inspector")]
app.add_plugin(WorldInspectorPlugin); //app.add_plugins(WorldInspectorPlugin::new());
app.add_startup_system(setup_ui).add_system(update_ui); //
app.add_systems(Startup, setup_ui)
.add_systems(Update, update_ui);
} }
} }