use bevy::{ecs::schedule::ScheduleLabel, prelude::*, color::palettes::css}; use springy::kinematic::Kinematic; const TICK_RATE: f64 = 1.0 / 60.0; const VISUAL_SLOWDOWN: f64 = 1.0; #[derive(ScheduleLabel, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] pub struct PhysicsSchedule; #[derive(Resource, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] pub struct Running(pub bool); pub fn physics_step(world: &mut World) { let running = world.resource::(); let input = world.resource::>(); if running.0 || input.just_pressed(KeyCode::KeyI) { world.run_schedule(PhysicsSchedule); } } pub fn toggle_running(mut running: ResMut, input: Res>) { if input.just_pressed(KeyCode::KeyP) { running.0 = !running.0; } } fn main() { let mut app = App::new(); app.insert_resource(ClearColor(css::DARK_GRAY.into())) .insert_resource(Msaa::default()) .add_plugins(DefaultPlugins) //.add_plugin(bevy_editor_pls::EditorPlugin::new()) .add_plugins(bevy_inspector_egui::quick::WorldInspectorPlugin::default()) .insert_resource(bevy_framepace::FramepaceSettings { limiter: bevy_framepace::Limiter::Manual(std::time::Duration::from_secs_f64(TICK_RATE)), ..default() }) .insert_resource(Running(false)) .add_systems(Startup, (setup_graphics, //setup_rope, setup_translation, setup_rotational, setup_rotation_test,)) .add_systems(Update, physics_step) .add_systems(PreUpdate, toggle_running) .register_type::() .register_type::() .register_type::() .register_type::() .register_type::(); app.add_systems(PostUpdate, ( symplectic_euler, spring_impulse.before(symplectic_euler), gravity.before(symplectic_euler), )); app.run(); } fn setup_graphics( mut commands: Commands, mut meshes: ResMut>, mut materials: ResMut>, ) { commands.spawn(PbrBundle { mesh: meshes.add(Plane3d::new(Vec3::Y, Vec2::splat(5.0))), material: materials.add(Color::from(Srgba::rgb(0.3, 0.5, 0.3))), ..default() }); commands.spawn(Camera3dBundle { transform: Transform::from_xyz(0.0, 3.0, 5.0).looking_at(Vec3::new(0.0, 2.0, 0.0), Vec3::Y), ..default() }); commands.spawn(PointLightBundle { point_light: PointLight { intensity: 1500.0, shadows_enabled: true, ..default() }, transform: Transform::from_xyz(4.0, 8.0, 4.0), ..default() }); } #[derive(Debug, Copy, Clone, Component)] pub struct Spring { pub containing: Entity, } #[derive(Default, Debug, Copy, Clone, Component, Reflect)] #[reflect(Component)] pub struct SpringSettings(springy::Spring); #[derive(Default, Debug, Copy, Clone, Component, Reflect)] #[reflect(Component)] pub struct Velocity { pub linear: Vec3, pub angular: Vec3, } #[derive(Default, Debug, Copy, Clone, Component, Reflect)] #[reflect(Component)] pub struct Impulse { pub linear: Vec3, pub angular: Vec3, } #[derive(Debug, Copy, Clone, Component, Reflect)] #[reflect(Component)] pub struct Inertia { pub linear: f32, pub angular: Vec3, } impl Default for Inertia { fn default() -> Self { Self { linear: 1.0, angular: Vec3::splat(0.05), } } } impl Inertia { pub const INFINITY: Self = Inertia { linear: f32::INFINITY, angular: Vec3::splat(f32::INFINITY), }; } #[derive(Debug, Copy, Clone, Component, Reflect)] #[reflect(Component)] pub struct Gravity(pub Vec3); impl Default for Gravity { fn default() -> Self { Self(Vec3::new(0.0, -9.817, 0.0)) } } /// Basic symplectic euler integration of the impulse/velocity/position. pub fn symplectic_euler( time: Res