diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 86f12e5b..1e38a783 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,7 +31,7 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - name: Run cargo test - run: cargo test --no-default-features --features enhanced-determinism,collider-from-mesh,serialize,bevy_xpbd_2d/2d,bevy_xpbd_3d/3d,bevy_xpbd_2d/f64,bevy_xpbd_3d/f64,default-collider,parry-f64,bevy_scene + run: cargo test --no-default-features --features enhanced-determinism,collider-from-mesh,serialize,avian2d/2d,avian3d/3d,avian2d/f64,avian3d/f64,default-collider,parry-f64,bevy_scene lints: name: Lints diff --git a/Cargo.toml b/Cargo.toml index a7456e4b..185ac159 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["crates/bevy_xpbd_2d", "crates/bevy_xpbd_3d"] +members = ["crates/avian2d", "crates/avian3d"] resolver = "2" [profile.dev] diff --git a/README.md b/README.md index 53b69a84..6b7676fb 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,19 @@ -# Bevy XPBD (soon deprecated in favor of [Avian](https://github.com/Jondolf/avian)) +# ![Avian Physics](https://raw.githubusercontent.com/Jondolf/bevy_xpbd/avian/assets/branding/logo.svg) -[![MIT/Apache 2.0](https://img.shields.io/badge/license-MIT%2FApache-blue.svg)](https://github.com/Jondolf/bevy_xpbd#license) -[![ci](https://github.com/Jondolf/bevy_xpbd/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/Jondolf/bevy_xpbd/actions/workflows/ci.yml) -[![2D crates.io](https://img.shields.io/crates/v/bevy_xpbd_2d?label=2D%20crates.io)](https://crates.io/crates/bevy_xpbd_2d) -[![2D docs.rs](https://img.shields.io/docsrs/bevy_xpbd_2d?label=2D%20docs.rs)](https://docs.rs/bevy_xpbd_2d) -[![3D crates.io](https://img.shields.io/crates/v/bevy_xpbd_3d?label=3D%20crates.io)](https://crates.io/crates/bevy_xpbd_3d) -[![3D docs.rs](https://img.shields.io/docsrs/bevy_xpbd_3d?label=3D%20docs.rs)](https://docs.rs/bevy_xpbd_3d) +[![MIT/Apache 2.0](https://img.shields.io/badge/license-MIT%2FApache-blue.svg)](https://github.com/Jondolf/avian#license) +[![ci](https://github.com/Jondolf/avian/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/Jondolf/avian/actions/workflows/ci.yml) +[![2D crates.io](https://img.shields.io/crates/v/avian2d?label=2D%20crates.io)](https://crates.io/crates/avian2d) +[![2D docs.rs](https://img.shields.io/docsrs/avian2d?label=2D%20docs.rs)](https://docs.rs/avian2d) +[![3D crates.io](https://img.shields.io/crates/v/avian3d?label=3D%20crates.io)](https://crates.io/crates/avian3d) +[![3D docs.rs](https://img.shields.io/docsrs/avian3d?label=3D%20docs.rs)](https://docs.rs/avian3d) -**⚠️ Bevy XPBD will be deprecated in favor of its successor [Avian](https://github.com/Jondolf/avian) (releases today or tomorrow). -No further development or maintenance will be done for Bevy XPBD. -See [#346](https://github.com/Jondolf/bevy_xpbd/issues/346) for background.** - ---- - -**Bevy XPBD** is a 2D and 3D physics engine based on _Extended Position Based Dynamics_ (XPBD) -for the [Bevy game engine](https://bevyengine.org/). +**Avian** is an ECS-based 2D and 3D physics engine for the [Bevy game engine](https://bevyengine.org/). --- ## Design -Below are some of the core design principles used in Bevy XPBD. +Below are some of the core design principles used in Avian. - **Made with Bevy, for Bevy.** No wrappers around existing engines. - **Provide an ergonomic and familiar API.** Ergonomics is key for a good experience. @@ -32,7 +25,7 @@ Below are some of the core design principles used in Bevy XPBD. ## Features -Below are some of the current features of Bevy XPBD. +Below are some of the current features of Avian. - Dynamic, kinematic and static rigid bodies - Linear and angular velocity @@ -41,17 +34,18 @@ Below are some of the current features of Bevy XPBD. - Linear and angular damping - Locking translational and rotational axes - Rigid body dominance + - Continuous Collision Detection (CCD) - Automatic deactivation with sleeping - Collision detection powered by [Parry](https://parry.rs) - Colliders with configurable collision layers, density, material properties and more + - Collider generation for meshes and entire scenes - Collision events - Access to colliding entities - Filtering and modifying collisions with custom systems - Manual contact queries and intersection tests - Constraints and joints - - Flexible API for creating position-based constraints - Several built-in joint types: fixed, distance, prismatic, revolute, spherical - - Support for custom joints and other constraints + - Support for custom joints and other constraints using XPBD - Spatial queries - Raycasting, shapecasting, point projection and intersection tests - Ergonomic component-based API for raycasts and shapecasts @@ -64,40 +58,42 @@ Below are some of the current features of Bevy XPBD. - `f32`/`f64` precision (`f32` by default) You can find a more complete list along with documentation in the -[Table of contents](https://docs.rs/bevy_xpbd_3d/latest/bevy_xpbd_3d/#table-of-contents) +[Table of contents](https://docs.rs/avian3d/latest/avian3d/#table-of-contents) on docs.rs. ## Documentation -- [2D documentation](https://docs.rs/bevy_xpbd_2d) -- [3D documentation](https://docs.rs/bevy_xpbd_3d) +- [2D documentation](https://docs.rs/avian2d) +- [3D documentation](https://docs.rs/avian3d) ## Usage example -First, add `bevy_xpbd_2d` or `bevy_xpbd_3d` to your dependencies in `Cargo.toml`: +First, add `avian2d` or `avian3d` to your dependencies in `Cargo.toml`: ```toml # For 2D applications: [dependencies] -bevy_xpbd_2d = "0.5" +avian2d = "0.1" # For 3D applications: [dependencies] -bevy_xpbd_3d = "0.5" +avian3d = "0.1" # If you want to use the most up-to-date version, you can follow the main branch: [dependencies] -bevy_xpbd_3d = { git = "https://github.com/Jondolf/bevy_xpbd", branch = "main" } +avian3d = { git = "https://github.com/Jondolf/avian", branch = "main" } ``` -Below is a very simple example where a box with initial angular velocity falls onto a plane. This is a modified version of Bevy's [3d_scene](https://bevyengine.org/examples/3d/3d-scene/) example. +Below is a very simple example where a cube with initial angular velocity falls onto a circular platform. +This is a modified version of Bevy's [`3d_scene`](https://bevyengine.org/examples/3D%20Rendering/3d-scene/) example. ```rust +use avian3d::prelude::*; use bevy::prelude::*; -use bevy_xpbd_3d::prelude::*; fn main() { App::new() + // Enable physics .add_plugins((DefaultPlugins, PhysicsPlugins::default())) .add_systems(Startup, setup) .run(); @@ -108,25 +104,25 @@ fn setup( mut meshes: ResMut>, mut materials: ResMut>, ) { - // Plane + // Static physics object with a collision shape commands.spawn(( RigidBody::Static, - Collider::cuboid(8.0, 0.002, 8.0), + Collider::cylinder(4.0, 0.1), PbrBundle { - mesh: meshes.add(Plane3d::default().mesh().size(8.0, 8.0)), - material: materials.add(Color::srgb(0.3, 0.5, 0.3)), + mesh: meshes.add(Cylinder::new(4.0, 0.1)), + material: materials.add(Color::WHITE), ..default() }, )); - // Cube + // Dynamic physics object with a collision shape and initial angular velocity commands.spawn(( RigidBody::Dynamic, - AngularVelocity(Vec3::new(2.5, 3.4, 1.6)), Collider::cuboid(1.0, 1.0, 1.0), + AngularVelocity(Vec3::new(2.5, 3.5, 1.5)), PbrBundle { - mesh: meshes.add(Cuboid::default()), - material: materials.add(Color::srgb(0.8, 0.7, 0.6)), + mesh: meshes.add(Cuboid::new(1.0, 1.0, 1.0)), + material: materials.add(Color::srgb_u8(124, 144, 255)), transform: Transform::from_xyz(0.0, 4.0, 0.0), ..default() }, @@ -135,7 +131,6 @@ fn setup( // Light commands.spawn(PointLightBundle { point_light: PointLight { - intensity: 2_000_000.0, shadows_enabled: true, ..default() }, @@ -145,17 +140,17 @@ fn setup( // Camera commands.spawn(Camera3dBundle { - transform: Transform::from_xyz(-4.0, 6.5, 8.0).looking_at(Vec3::ZERO, Vec3::Y), + transform: Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Dir3::Y), ..default() }); } ``` - +![A spinning cube falling onto a circular platform](https://github.com/Jondolf/bevy_xpbd/assets/57632562/d53197fc-e142-4eb9-a762-dc16f6cdb1dd) ## More examples -You can find lots of 2D and 3D examples in [/crates/bevy_xpbd_2d/examples](/crates/bevy_xpbd_2d/examples) and [/crates/bevy_xpbd_3d/examples](/crates/bevy_xpbd_3d/examples) respectively. +You can find lots of 2D and 3D examples in [/crates/avian2d/examples](/crates/avian2d/examples) and [/crates/avian3d/examples](/crates/avian3d/examples) respectively. The examples support both `f32` and `f64` precisions, so the code contains some feature-dependent types like `Scalar` and `Vector`. In actual usage these are not needed, so you can just use `f32` or `f64` types depending on the features you have chosen. @@ -170,23 +165,27 @@ cargo run --example cubes --no-default-features --features "3d f64 parry-f64" ## Supported Bevy versions -**Note**: Bevy XPBD is deprecated and won't support future versions of Bevy. -Instead, it is recommended to use its successor [Avian](https://github.com/Jondolf/avian). +| Bevy | Avian | +| ---- | ----- | +| 0.14 | 0.1 | + +
+ Bevy XPBD versions (the predecessor of Avian) -| Bevy | Bevy XPBD | -| ---- | --------- | -| 0.14 | 0.5 | -| 0.13 | 0.4 | -| 0.12 | 0.3 | -| 0.11 | 0.2 | -| 0.10 | 0.1 | + | Bevy | Bevy XPBD | + | ---- | --------- | + | 0.14 | 0.5 | + | 0.13 | 0.4 | + | 0.12 | 0.3 | + | 0.11 | 0.2 | + | 0.10 | 0.1 | +
## Future features -- Continuous collision detection (CCD) - Per-entity collision hooks or callbacks - Flags for what types of collisions are active, like collisions against specific rigid body types, sensors or parents -- Performance optimization (better broad phase, parallel solver...) +- Performance optimization (better broad phase, parallel solver, proper SIMD...) - Joint motors - Articulations, aka. multibody joints - Proper cross-platform determinism @@ -200,7 +199,7 @@ For larger changes and additions, it's better to open an issue or ask me for inp before making a pull request. You can also ask for help or ask questions on the [Bevy Discord](https://discord.com/invite/gMUk5Ph) -server's `bevy_xpbd` thread in `#crate-help`. My username on the Discord is `Jondolf` (`@jondolfdev`). +server's `avian` thread in `#crate-help`. My username on the Discord is `Jondolf` (`@jondolfdev`). ## Acknowledgements @@ -211,7 +210,7 @@ keeps me motivated to build the best engine I can. I would also like to give a special thanks to [Johan Helsing][johan-helsing] for inspiring this project and helping me significantly in the early stages. -His original [tutorial series][johan-xpbd-tutorial] is the reason `bevy_xpbd` +His original [tutorial series][johan-xpbd-tutorial] is the reason `avian` exists in the first place, and without his support and contributions, the project wouldn't be anywhere near where it is today. @@ -220,7 +219,7 @@ the project wouldn't be anywhere near where it is today. ## License -Bevy XPBD is free and open source. All code in this repository is dual-licensed under either: +Avian is free and open source. All code in this repository is dual-licensed under either: - MIT License ([LICENSE-MIT](/LICENSE-MIT) or ) - Apache License, Version 2.0 ([LICENSE-APACHE](/LICENSE-APACHE) or ) diff --git a/assets/branding/icon.png b/assets/branding/icon.png new file mode 100644 index 00000000..317c529a Binary files /dev/null and b/assets/branding/icon.png differ diff --git a/assets/branding/logo.svg b/assets/branding/logo.svg new file mode 100644 index 00000000..a5e84f5d --- /dev/null +++ b/assets/branding/logo.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/crates/bevy_xpbd_2d/Cargo.toml b/crates/avian2d/Cargo.toml similarity index 75% rename from crates/bevy_xpbd_2d/Cargo.toml rename to crates/avian2d/Cargo.toml index 4354d658..473fd652 100644 --- a/crates/bevy_xpbd_2d/Cargo.toml +++ b/crates/avian2d/Cargo.toml @@ -1,19 +1,16 @@ [package] -name = "bevy_xpbd_2d" -version = "0.5.0" +name = "avian2d" +version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" authors = ["Joona Aalto "] -description = "Deprecated, use `avian2d` instead. A physics engine based on Extended Position Based Dynamics for the Bevy game engine" -documentation = "https://docs.rs/bevy_xpbd_2d" -repository = "https://github.com/Jondolf/bevy_xpbd" +description = "A physics engine based on Extended Position Based Dynamics for the Bevy game engine" +documentation = "https://docs.rs/avian2d" +repository = "https://github.com/Jondolf/avian" readme = "README.md" keywords = ["gamedev", "physics", "simulation", "xpbd", "bevy"] categories = ["game-development", "science", "simulation"] -[badges] -maintenance = { status = "deprecated" } - [features] default = ["2d", "f32", "parry-f32", "debug-plugin", "parallel", "bevy_scene"] 2d = [] @@ -24,6 +21,7 @@ debug-plugin = ["bevy/bevy_gizmos", "bevy/bevy_render"] simd = ["parry2d?/simd-stable", "parry2d-f64?/simd-stable"] parallel = ["parry2d?/parallel", "parry2d-f64?/parallel"] enhanced-determinism = [ + "dep:libm", "parry2d?/enhanced-determinism", "parry2d-f64?/enhanced-determinism", "bevy_math/libm", @@ -44,14 +42,16 @@ serialize = [ ] [lib] -name = "bevy_xpbd_2d" +name = "avian2d" path = "../../src/lib.rs" required-features = ["2d"] +bench = false [dependencies] -bevy_xpbd_derive = { path = "../bevy_xpbd_derive", version = "0.1" } -bevy = { version = "0.14", default-features = false } -bevy_math = { version = "0.14" } +avian_derive = { path = "../avian_derive", version = "0.1" } +bevy = { version = "0.14.0-rc", default-features = false } +bevy_math = { version = "0.14.0-rc" } +libm = { version = "0.2", optional = true } parry2d = { version = "0.15", optional = true } parry2d-f64 = { version = "0.15", optional = true } nalgebra = { version = "0.32.6", features = [ @@ -66,8 +66,10 @@ bitflags = "2.5.0" [dev-dependencies] examples_common_2d = { path = "../examples_common_2d" } -bevy_math = { version = "0.14", features = ["approx"] } +benches_common_2d = { path = "../benches_common_2d" } +bevy_math = { version = "0.14.0-rc", features = ["approx"] } approx = "0.5" +criterion = { version = "0.5", features = ["html_reports"] } insta = "1.0" [[example]] @@ -78,6 +80,10 @@ required-features = ["2d", "default-collider"] name = "kinematic_character_2d" required-features = ["2d", "default-collider"] +[[example]] +name = "ccd" +required-features = ["2d", "default-collider"] + [[example]] name = "chain_2d" required-features = ["2d", "default-collider"] @@ -109,3 +115,8 @@ required-features = ["2d", "default-collider"] [[example]] name = "revolute_joint_2d" required-features = ["2d", "default-collider"] + +[[bench]] +name = "pyramid" +required-features = ["2d", "default-collider"] +harness = false diff --git a/crates/avian2d/benches/pyramid.rs b/crates/avian2d/benches/pyramid.rs new file mode 100644 index 00000000..adac8819 --- /dev/null +++ b/crates/avian2d/benches/pyramid.rs @@ -0,0 +1,63 @@ +use std::time::Duration; + +use avian2d::math::*; +use avian2d::prelude::*; +use benches_common_2d::bench_app; +use bevy::prelude::*; +use criterion::{criterion_group, criterion_main, Criterion}; + +fn setup(app: &mut App, base_count: u32) { + app.insert_resource(SubstepCount(8)); + app.add_systems(Startup, move |mut commands: Commands| { + // Ground + commands.spawn(( + RigidBody::Static, + Collider::rectangle(800.0, 40.0), + Position::from_xy(0.0, -20.0), + )); + + let h = 0.5; + let box_size = 2.0 * h; + let collider = Collider::rectangle(box_size, box_size); + let shift = h; + for i in 0..base_count { + let y = (2.0 * i as Scalar + 1.0) * shift * 0.99; + + for j in i..base_count { + let x = (i as Scalar + 1.0) * shift + 2.0 * (j - i) as Scalar * shift + - h * base_count as Scalar; + + commands.spawn(( + RigidBody::Dynamic, + collider.clone(), + Position::from_xy(x, y), + )); + } + } + }); +} + +fn criterion_benchmark(c: &mut Criterion) { + c.bench_function("pyramid with base of 20 boxes, 5 steps", |b| { + bench_app(b, 5, |app| setup(app, 20)) + }); + + c.bench_function("pyramid with base of 40 boxes, 5 steps", |b| { + bench_app(b, 5, |app| setup(app, 40)) + }); + + c.bench_function("pyramid with base of 60 boxes, 5 steps", |b| { + bench_app(b, 5, |app| setup(app, 60)) + }); + + c.bench_function("pyramid with base of 80 boxes, 5 steps", |b| { + bench_app(b, 5, |app| setup(app, 80)) + }); +} + +criterion_group!( + name = benches; + config = Criterion::default().measurement_time(Duration::from_secs(10)); + targets = criterion_benchmark +); +criterion_main!(benches); diff --git a/crates/avian2d/examples/ccd.rs b/crates/avian2d/examples/ccd.rs new file mode 100644 index 00000000..42f7686c --- /dev/null +++ b/crates/avian2d/examples/ccd.rs @@ -0,0 +1,199 @@ +//! Demonstrates Continuous Collision Detection (CCD) to prevent tunneling. +//! +//! Two forms of CCD are supported: +//! +//! 1. Speculative collision +//! - Predicts approximate contacts before they happen. +//! - Enabled by default for all rigid bodies. +//! - Very efficient and relatively reliable. +//! - Can sometimes cause ghost collisions. +//! - Can sometimes miss collisions against objects spinning at very high speeds. +//! +//! 2. Swept CCD +//! - Sweeps colliders from their previous positions to their current ones, +//! and if a hit is found, moves the bodies to the time of impact. +//! - Enabled for rigid bodies with the `SweptCcd` component. +//! - Can prevent tunneling completely. More reliable than speculative collision. +//! - More expensive than speculative collision. +//! - Can cause "time loss" where bodies appear to stop for a moment +//! because they are essentially brought back in time. +//! - Two modes: +//! 1. Linear: Only considers translational motion. +//! 2. Non-linear: Considers both translation and rotation. More expensive. + +use avian2d::{math::*, prelude::*}; +use bevy::{prelude::*, sprite::MaterialMesh2dBundle}; +use examples_common_2d::ExampleCommonPlugin; + +fn main() { + App::new() + .add_plugins(( + DefaultPlugins, + ExampleCommonPlugin, + PhysicsPlugins::default(), + )) + .insert_resource(ClearColor(Color::srgb(0.05, 0.05, 0.1))) + .insert_resource(Gravity(Vector::NEG_Y * 9.81 * 100.0)) + .add_systems(Startup, setup) + .add_systems(Update, update_config) + .add_systems(PhysicsSchedule, spawn_balls.in_set(PhysicsStepSet::First)) + .run(); +} + +#[derive(Component)] +struct SpeculativeCollisionText; + +#[derive(Component)] +struct SweptCcdText; + +fn setup(mut commands: Commands) { + commands.spawn(Camera2dBundle::default()); + + // Add two kinematic bodies spinning at high speeds. + commands.spawn(( + SpriteBundle { + sprite: Sprite { + color: Color::srgb(0.7, 0.7, 0.8), + custom_size: Some(Vec2::new(1.0, 400.0)), + ..default() + }, + transform: Transform::from_xyz(-200.0, -200.0, 0.0), + ..default() + }, + RigidBody::Kinematic, + AngularVelocity(25.0), + Collider::rectangle(1.0, 400.0), + // Enable swept CCD for this body. Considers both translational and rotational motion by default. + // This could also be on the ball projectiles. + SweptCcd::default(), + )); + commands.spawn(( + SpriteBundle { + sprite: Sprite { + color: Color::srgb(0.7, 0.7, 0.8), + custom_size: Some(Vec2::new(1.0, 400.0)), + ..default() + }, + transform: Transform::from_xyz(200.0, -200.0, 0.0), + ..default() + }, + RigidBody::Kinematic, + AngularVelocity(-25.0), + Collider::rectangle(1.0, 400.0), + // Enable swept CCD for this body. Considers both translational and rotational motion by default. + // This could also be on the ball projectiles. + SweptCcd::default(), + )); + + // Setup help text. + let text_style = TextStyle { + font_size: 20.0, + ..default() + }; + commands.spawn(( + SpeculativeCollisionText, + TextBundle::from_sections([ + TextSection::new("(1) Speculative Collision: ", text_style.clone()), + TextSection::new("On", text_style.clone()), + ]) + .with_style(Style { + position_type: PositionType::Absolute, + top: Val::Px(30.0), + left: Val::Px(10.0), + ..default() + }), + )); + commands.spawn(( + SweptCcdText, + TextBundle::from_sections([ + TextSection::new("(2) Swept CCD: ", text_style.clone()), + TextSection::new( + "Non-linear (considers both translation and rotation)", + text_style, + ), + ]) + .with_style(Style { + position_type: PositionType::Absolute, + top: Val::Px(50.0), + left: Val::Px(10.0), + ..default() + }), + )); +} + +/// Spawns balls moving at the spinning objects at high speeds. +fn spawn_balls( + mut commands: Commands, + mut materials: ResMut>, + mut meshes: ResMut>, + time: Res