From 49059b6f3b37dcb9ed11ef2b9981affe7cdc5904 Mon Sep 17 00:00:00 2001 From: Jondolf Date: Fri, 16 Jun 2023 21:07:56 +0300 Subject: [PATCH 01/29] Improve crate level docs and add `#[warn(missing_docs)]` --- src/lib.rs | 209 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 203 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5b82a82b..a3495de3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,15 +1,212 @@ //! # Bevy XPBD //! -//! **Bevy XPBD** is a 2D and 3D physics engine based on *Extended Position Based Dynamics* (XPBD) for the [Bevy game engine](https://bevyengine.org/). The *Entity Component System* (ECS) is used heavily throughout the engine to enable enhanced parallellism, configurability and familiarity, while making the engine fit better into the Bevy ecosystem. +//! **Bevy XPBD** is a 2D and 3D physics engine based on [*Extended Position Based Dynamics* (XPBD)](#What_is_XPBD?) +//! for the [Bevy game engine](https://bevyengine.org/). //! -//! XPBD is an improved variant of traditional *Position Based Dynamics* (PBD). -//! It provides unconditionally stable, time step independent and physically accurate simulations that use simple constraint projection to handle things like contacts, joints, and interactions between rigid bodies, soft bodies and fluids. +//! ## Features //! -//! To understand the algorithm better, it's worth checking out some of the academic papers: +//! Below are some of the features of Bevy XPBD. //! -//! - Müller M, Macklin M, Chentanez N, Jeschke S, Kim T. 2020. *[Detailed Rigid Body Simulation with Extended Position Based Dynamics](https://matthias-research.github.io/pages/publications/PBDBodies.pdf)*. +//! - 2D and 3D support with `bevy_xpbd_2d` and `bevy_xpbd_3d` +//! - Dynamic, kinematic and static [rigid bodies](RigidBody) +//! - [Gravity](Gravity) +//! - [External forces](ExternalForce) and [torque](ExternalTorque) +//! - [Colliders](Collider) powered by [parry](parry) +//! - Material properties like [restitution](Restitution) and [friction](Friction) +//! - Configurable [timesteps](PhysicsTimestep) +//! - Highly modular [plugin architecture](plugins) +//! - Choose number precision with the `f32`/`f64` feature (`f32` by default) +//! - Built-in [constraints] and support for [custom constraints](constraints#custom-constraints) +//! - [Joints](joints) +//! - [Substepping](constraints#substepping) +//! - Automatically deactivating bodies with [sleeping](sleeping) to improve performance //! -//! - Macklin M, Müller M, Chentanez N. 2016. *[XPBD: Position-Based Simulation of Compliant Constrained Dynamics](http://mmacklin.com/xpbd.pdf)*. +//! ## Getting started +//! +//! This short guide should help you get started with Bevy XPBD. +//! +//! ### Adding the dependency +//! +//! For a 2D game, add the `bevy_xpbd_2d` crate to your `Cargo.toml` like this: +//! +//! ```toml +//! [dependencies] +//! bevy_xpbd_2d = "0.1" +//! ``` +//! +//! Similarly for a 3D game, add `bevy_xpbd_3d`: +//! +//! ```toml +//! [dependencies] +//! bevy_xpbd_3d = "0.1" +//! ``` +//! +//! By default, Bevy XPBD uses `f32` numbers. If you encounter instability or use small [timesteps](PhysicsTimestep), +//! you might want to use `f64` instead. You can change these kinds of features by disabling +//! the default features and manually specifying the feature flags you want: +//! +//! ```toml +//! [dependencies] +//! # Add 3D Bevy XPBD with double-precision floating point numbers +//! bevy_xpbd_3d = { version = "0.1", features = ["3d", "f64"], default-features = false } +//! ``` +//! +//! ### Feature flags +//! +//! - `2d` enables simulation on the `x` and `y` axes. Enabled by default for `bevy_xpbd_2d`. Incompatible with `3d`. +//! - `3d` enables simulation on the `x`, `y` and `z` axes. Enabled by default for `bevy_xpbd_3d`. Incompatible with `2d`. +//! - `f32` enables using `f32` numbers. Incompatible with `f64`. +//! - `f64` enables using `f64` numbers. Recommended when encountering stability problems, especially with +//! small timesteps. Incompatible with `f32`. +//! - `simd` enables [SIMD](https://en.wikipedia.org/wiki/Single_instruction,_multiple_data) optimizations. +//! - `enhanced-determinism` enables increased determinism. (Note: cross-platform determinism doesn't work yet, even +//! with this feature enabled) +//! +//! ### Install the plugin +//! +//! Bevy XPBD is designed to be very modular. It is built from many different [plugins] that +//! manage different parts of the engine. These plugins can be easily initialized and configured through +//! the [`PhysicsPlugins`] plugin group. +//! +//! ``` +//! use bevy::prelude::*; +//! use bevy_xpbd_3d::prelude::*; +//! +//! App::new() +//! .add_plugins(DefaultPlugins) +//! .add_plugins(PhysicsPlugins) +//! // ...your other plugins, systems and resources +//! .run(); +//! ``` +//! +//! Now you can use all of Bevy XPBD's [components] and [resources] to build whatever you want! +//! +//! For example, adding a [rigid body](RigidBody) with a [collider](Collider) is as simple as spawning an entity +//! with the [`RigidBody`] and [`Collider`] components: +//! +//! ``` +//! commands.spawn((RigidBody::Dynamic, Collider::ball(0.5))); +//! ``` +//! +//! To learn more about using Bevy XPBD, consider taking a look at the official [examples](#examples) and +//! how to accomplish some [common tasks](#common-tasks). +//! +//! To learn more about the structure of the engine, consider taking a look at the [plugins] and +//! [what XPBD actually is](#what-is-xpbd). +//! +//! ### Examples +//! +//! You can find 2D examples in [`crates/bevy_xpbd_2d/examples`](https://github.com/Jondolf/bevy_xpbd/tree/main/crates/bevy_xpbd_2d/examples) +//! and 3D examples in [`crates/bevy_xpbd_3d/examples`](https://github.com/Jondolf/bevy_xpbd/tree/main/crates/bevy_xpbd_3d/examples). +//! +//! You can run the examples with default features as you normally would. +//! For example, running the `cubes` example looks like this: +//! +//! ```bash +//! cargo run --example cubes +//! ``` +//! +//! ### Common tasks +//! +//! - [Create a rigid body](RigidBody) +//! - [Add a collider](Collider) +//! - [Define mass properties](RigidBody#mass-properties) +//! - [Use joints](joints) +//! - [Configure gravity](Gravity) +//! - [Configure the physics timestep](PhysicsTimestep) +//! - [Create custom constraints](constraints) +//! - [Replace built-in plugins with custom plugins](plugins#custom-plugins) +//! +//! ## What is XPBD? +//! +//! *XPBD* or *Extended Position Based Dynamics* is a physics simulation method that extends +//! the traditional *PBD* to be more physically accurate and less dependent on time step size +//! and iteration count. +//! +//! At a high level, XPBD consists of a broad phase followed by a substepping loop that handles position +//! [integration](integrator), [constraint solving](solver), and velocity updates. Unlike in force or impulse +//! based simulation methods, [constraints] operate directly on positions, which often provides more reliable +//! and stable results, and allows straightforward coupling of [rigid bodies](RigidBody), soft bodies and fluids. +//! +//! Below is a high level overview of the XPBD simulation loop. +//! +//! ``` +//! while simulating: +//! // Substep size +//! h = ∆t / numSubsteps +//! +//! // Broad phase +//! collect_collision_pairs() +//! +//! for substep_count: +//! // Integrate +//! for n particles and bodies: +//! // Integrate position +//! x_prev = x +//! v = v + h * f_ext / m +//! x = x + h * v +//! +//! // Integrate rotation +//! q_prev = q +//! ω = ω + h * I^-1 * (τ_ext - (ω x (I * ω))) +//! q = q + h * 0.5 * [ω_x, ω_y, ω_z, 0] * q +//! q = q / |q| +//! +//! // Solve constraints (1 iteration and many substeps recommended) +//! for iteration_count: +//! solve_constraints(particles and bodies) +//! +//! // Update velocities +//! for n particles and bodies: +//! v = (x - x_prev) / h +//! ∆q = q * q_prev^-1 +//! ω = 2 * [∆q_x, ∆q_y, ∆q_z] / h +//! ω = ∆q_w >= 0 ? ω : -ω +//! +//! // Solve velocity constraints (dynamic friction and restitution) +//! solve_velocities(particles and bodies) +//! ``` +//! +//! where `h` is the [substep size](SubDeltaTime), `q` is the [rotation](Rot) as a quaternion, +//! `ω` is the [angular velocity](AngVel), `I` is the [angular inertia tensor](`Inertia`) and `τ` is the +//! [external torque](ExternalTorque). +//! +//! In Bevy XPBD, the simulation loop is handled by various plugins. The [`PhysicsSetupPlugin`] sets up +//! the Bevy schedules[^1][^2] and sets[^3][^4], the [`BroadPhasePlugin`] manages the broad phase, the [`IntegratorPlugin`] handles +//! XPBD integration, and so on. You can find all of the plugins and their responsibilities [here](plugins). +//! +//! ### See also +//! +//! - [XPBD integration step](integrator) +//! - [Constraints and how to create them](constraints) +//! - [Schedules and sets used for the simulation loop](PhysicsSetupPlugin) +//! +//! ### Learning resources +//! +//! If you want to learn more about XPBD, I recommend taking a look at some of the papers. +//! Especially the first one from 2020 was used heavily for the simulation loop and constraints in Bevy XPBD. +//! +//! - XPBD: Müller M, Macklin M, Chentanez N, Jeschke S, Kim T. 2020. *[Detailed Rigid Body Simulation with Extended Position Based Dynamics](https://matthias-research.github.io/pages/publications/PBDBodies.pdf)*. +//! - XPBD: Macklin M, Müller M, Chentanez N. 2016. *[XPBD: Position-Based Simulation of Compliant Constrained Dynamics](http://mmacklin.com/xpbd.pdf)*. +//! +//! The papers are quite academic, so you might instead prefer some videos and articles. +//! The first one by Ten Minute Physics (Matthias Müller, one of the XPBD researchers) is great for understanding +//! how XPBD differs from other simulation methods and how the constraints work. +//! +//! - Video: Ten Minute Physics. 2022. *[Getting ready to simulate the world with XPBD](https://youtu.be/jrociOAYqxA)*. +//! - Tutorial series: Johan Helsing. *[Tutorial: Making a physics engine with Bevy](https://johanhelsing.studio/posts/bevy-xpbd)*. +//! (inspired this project) +//! +//! [^1]: [`PhysicsSchedule`] +//! +//! [^2]: [`SubstepSchedule`] +//! +//! [^3]: [`PhysicsSet`] +//! +//! [^4]: [`SubsteppingSet`] + +#![allow(rustdoc::invalid_rust_codeblocks)] +#![warn(missing_docs)] #[cfg(all(feature = "f32", feature = "f64"))] compile_error!("feature \"f32\" and feature \"f64\" cannot be enabled at the same time"); From 49e75911a697fa582ac19afe6f04a68595a3730b Mon Sep 17 00:00:00 2001 From: Jondolf Date: Fri, 16 Jun 2023 21:08:31 +0300 Subject: [PATCH 02/29] Fix `solve_constraint` docs --- src/plugins/solver.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/solver.rs b/src/plugins/solver.rs index 3ba8190e..e4f37f07 100644 --- a/src/plugins/solver.rs +++ b/src/plugins/solver.rs @@ -132,7 +132,7 @@ fn penetration_constraints( /// /// ## User constraints /// -/// To create a new constraint, implement [`XpbdConstraint`] for a component, get the [`XpbdSubstepSchedule`] and add this system into +/// To create a new constraint, implement [`XpbdConstraint`] for a component, get the [`SubstepSchedule`] and add this system into /// the [`SubsteppingSet::SolveUserConstraints`] set. /// You must provide the number of entities in the constraint using generics. /// @@ -140,8 +140,8 @@ fn penetration_constraints( /// /// ```rust,ignore /// let substeps = app -/// .get_schedule_mut(XpbdSubstepSchedule) -/// .expect("add XpbdSubstepSchedule first"); +/// .get_schedule_mut(SubstepSchedule) +/// .expect("add SubstepSchedule first"); /// /// substeps.add_system( /// solve_constraint:: From ef106e2cf74b1a7129c06f83773015d4d9ead266 Mon Sep 17 00:00:00 2001 From: Jondolf Date: Fri, 16 Jun 2023 21:09:15 +0300 Subject: [PATCH 03/29] Add docs for `constraints` --- src/constraints/mod.rs | 163 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 162 insertions(+), 1 deletion(-) diff --git a/src/constraints/mod.rs b/src/constraints/mod.rs index f92cf2a1..1b0cc268 100644 --- a/src/constraints/mod.rs +++ b/src/constraints/mod.rs @@ -1,4 +1,165 @@ -//! General constraint logic and different types of constraints, such as joints and penetration constraints. +//! Contains constraints used by the [solver]. +//! +//! # Constraints +//! +//! **Constraints** are a way to model physical relationships between entities. They are an integral part of XPBD, and they can be used +//! for things like [contact resolution](penetration::PenetrationConstraint), [joints], soft bodies, and much more. +//! +//! At its core, a constraint is just a rule that is enforced by moving the participating entities in a way that satisfies that rule. +//! For example, a distance constraint is satisfied when the distance between two entities is equal to the desired distance. +//! +//! Most constraints in Bevy XPBD are modeled as seperate entities with a component that implements [`XpbdConstraint`]. +//! They contain a `solve` method that receives the states of the participating entities as parameters. +//! You can find more details on how to use each constraint by taking a look at their documentation. +//! +//! Below are the currently implemented constraints. +//! +//! - [`PenetrationConstraint`](penetration::PenetrationConstraint) +//! - [Joints](joints) +//! - [`FixedJoint`] +//! - [`SphericalJoint`] +//! - [`RevoluteJoint`] +//! - [`PrismaticJoint`] +//! +//! More constraint types will be added in future releases. If you need more constraints now, consider +//! [creating your own constraints](custom-constraints). +//! +//! ## Custom constraints +//! +//! In Bevy XPBD, you can easily create your own constraints using the same APIs that the engine uses for its own constraints. +//! +//! First, create a struct and implement the [`XpbdConstraint`] trait, giving the number of participating entities using generics. +//! It should look similar to this: +//! +//! ```ignore +//! struct CustomConstraint { +//! entity1: Entity, +//! entity2: Entity, +//! lagrange: f32, +//! } +//! +//! impl XpbdConstraint<2> for CustomConstraint { +//! fn entities(&self) -> [Entity; 2] { +//! [self.entity1, self.entity2] +//! } +//! fn clear_lagrange_multipliers(&mut self) { +//! self.lagrange = 0.0; +//! } +//! fn solve(&mut self, bodies: [&mut RigidBodyQueryItem; 2], dt: Scalar) { +//! // Constraint solving logic goes here +//! } +//! } +//! ``` +//! +//! Take a look at [`XpbdConstraint`] and the constraint [theory](#theory) to learn more about what to put in `solve`. +//! +//! Next, we need to add a system that solves the constraint during each run of the [solver]. If your constraint is +//! a component like most of Bevy XPBD's constraints, you can use the generic [`solve_constraint`] system that handles +//! some of the background work for you. +//! +//! Add the `solve_constraint::` system to the +//! [substepping schedule's](SubstepSchedule) [`SubsteppingSet::SolveUserConstraints`] set. It should look like this: +//! +//! ```ignore +//! // Get substep schedule +//! let substeps = app +//! .get_schedule_mut(SubstepSchedule) +//! .expect("add SubstepSchedule first"); +//! +//! // Add custom constraint +//! substeps.add_system( +//! solve_constraint::.in_set(SubsteppingSet::SolveUserConstraints), +//! ); +//! ``` +//! +//! Now just spawn an instance of the constraint, give it the participating entities, and the constraint should be getting +//! solved automatically according to the `solve` method! +//! +//! ## Theory +//! +//! In this section, you can learn some of the theory behind how constraints work. Understanding the theory and maths isn't +//! important for using constraints, but it can be useful if you want to [create your own constraints](#custom-constraints). +//! +//! **Note**: In the following theory, I primarily use the word "particle", but the same logic applies to normal +//! [rigid bodies](RigidBody) as well. However, unlike particles, rigid bodies can also have angular quantities such as +//! [rotation](Rot) and [angular inertia](Inertia), so constraints can also affect their orientation. This is explained +//! in more detail [at the end](#rigid-body-constraints). +//! +//! ### Constraint functions +//! +//! At the mathematical level, each constraint has a *constraint function* `C(x)` that takes the state +//! of the particles as parameters and outputs a scalar value. The goal of the constraint is to move the particles +//! in a way that the output *satisfies* a constraint equation. +//! +//! For *equality constraints* the equation takes the form `C(x) = 0`. In other words, the constraint tries to +//! *minimize* the value of `C(x)` to be as close to zero as possible. When the equation is true, the constraint is *satisfied*. +//! +//! For a distance constraint, the constraint function would be `C(x) = distance - rest_distance`, +//! because this would be zero when the distance is equal to the desired rest distance. +//! +//! For *inequality constraints* the equation instead takes the form `C(x) >= 0`. These constraints are only applied +//! when `C(x) < 0`, which is useful for things like static friction and [joint limits](joints#joint-limits). +//! +//! ### Constraint gradients +//! +//! To know what direction the constraint should move the particles, they use a *constraint gradient* `▽C(x)`. +//! It is a vector that point in the direction in which the constraint function value `C` increases the most. +//! The length of the gradient indicates how much `C` changes when moving a body by one unit. This is often equal to one. +//! +//! In a case where two particles are being constrained by a distance constraint, and the particles are outside of the +//! rest distance, the gradient vector would point away from the other particle, because it would increase the distance +//! even further. +//! +//! ### Lagrange multiplier +//! +//! In the context of constraints, the Lagrange multiplier `λ` corresponds to the signed magnitude of the constraint force. +//! It is a scalar value that is the same for all of the constraint's participating particles, and it is used for computing +//! the correction that the constraint should apply to the particles along the gradients. +//! +//! In XPBD, the Lagrange multiplier update `Δλ` during a substep is computed by dividing the opposite of `C` +//! by the sum of the products of the inverse masses and squared gradient lengths plus an additional compliance term: +//! +//! ```text +//! Δλ = -C / (sum(w_i * |▽C_i|^2) + α / h^2) +//! ``` +//! +//! where `w_i` is the inverse mass of particle `i`, `|▽C_i|` is the length of the gradient vector for particle `i`, +//! `α` is the constraint's compliance (inverse of stiffness) and `h` is the [substep size](SubDeltaTime). Using `α = 0` +//! corresponds to infinite stiffness. +//! +//! The minus sign is there because the gradients point in the direction in which `C` increases the most, +//! and we instead want to minimize `C`. +//! +//! Note that if the gradients are normalized, as is often the case, the squared gradient lengths can be omitted from the +//! calculation. +//! +//! ### Solving constraints +//! +//! Once we have computed the Lagrange multiplier `λ`, we can compute the positional correction for a given particle +//! as the product of the Lagrange multiplier and the particle's inverse mass and gradient vector: +//! +//! ```text +//! Δx_i = Δλ * w_i * ▽C_i +//! ``` +//! +//! In other words, we typically move the particle along the gradient by `λ` proportional to the particle's inverse mass. +//! +//! ### Rigid body constraints +//! +//! Unlike particles, [rigid bodies](RigidBody) also have angular quantities like [rotation](Rot), +//! [angular velocity](AngVel) and [angular inertia](Inertia). In addition, constraints can be applied at specific +//! points in the body, like contact positions or joint attachment positions, which also affects the orientation. +//! +//! If the constraint function's value is the rotation angle and the gradient vector is the rotation axis, +//! we can compute the angular correction for a given body like this: +//! +//! ```text +//! Δq_i = 0.5 * [I_i^-1 * (r_i x (Δλ * ▽C_i)), 0] * q_i +//! ``` +//! +//! where `q_i` is the rotation of body `i`, `I_i^-1` is the inverse inertia tensor, and `r_i` is a vector pointing +//! from the body's center of mass to some attachment position. Note that the value of the +//! inertia tensor depends on the orientation of the body, so it should be recomputed each time the constraint is solved. pub mod joints; pub mod penetration; From 0cd0e183d9d6f57c346ecfe021befd2d57c78a45 Mon Sep 17 00:00:00 2001 From: Jondolf Date: Fri, 16 Jun 2023 21:09:41 +0300 Subject: [PATCH 04/29] Improve `RigidBody` docs --- src/components/mod.rs | 58 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/src/components/mod.rs b/src/components/mod.rs index 6b41bcf5..9a29e699 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -14,7 +14,63 @@ use crate::prelude::*; use bevy::prelude::*; use derive_more::From; -/// The rigid body type. A rigid body can be either dynamic, kinematic or static. +/// A non-deformable body used for the simulation of most physics objects. +/// +/// A rigid body can be either dynamic, kinematic or static. +/// +/// - **Dynamic bodies** are similar to real life objects and are affected by forces and contacts. +/// - **Kinematic bodies** can only be moved programmatically, which is useful for things like character controllers and moving platforms. +/// - **Static bodies** can not move, so they can be good for objects in the environment like the ground and walls. +/// +/// ## Creation +/// +/// Creating a rigid body is as simple as adding the [`RigidBody`] component: +/// +/// ``` +/// commands.spawn(RigidBody::Dynamic); +/// ``` +/// +/// Bevy XPBD will automatically add any missing components, like the following: +/// +/// - [`Pos`] +/// - [`Rot`] +/// - [`LinVel`] +/// - [`AngVel`] +/// - [`ExternalForce`] +/// - [`ExternalTorque`] +/// - [`Friction`] +/// - [`Restitution`] +/// - [`Mass`] +/// - [`Inertia`] +/// - [`LocalCom`] +/// +/// You can change any of these during initialization and runtime in order to alter the behaviour of the body. +/// +/// Note that by default, rigid bodies don't have any mass, so dynamic bodies will gain infinite velocity upon any interaction. +/// See the [section below](#adding-mass-properties] for how to add mass properties. +/// +/// ## Adding mass properties +/// +/// You should always give dynamic rigid bodies mass properties. The easiest way to do this is to [add a collider](collider), since colliders +/// by default have [their own mass properties](ColliderMassProperties) that are added to the body's own mass properties. +/// +/// ``` +/// // The mass properties will be computed from a ball shape with a radius of 0.5 and a density of 1. +/// commands.spawn((RigidBody::Dynamic, Collider::ball(0.5))); +/// ``` +/// +/// If you don't want to add a collider, you can instead add a [`MassPropsBundle`] with the mass properties computed from a collider +/// shape using the [`MassPropsBundle::new_computed`](MassPropsBundle#method.new_computed) method. +/// +/// ``` +/// // This is equivalent to the earlier approach, but no collider will be added. +/// commands.spawn((RigidBody::Dynamic, MassPropsBundle::new_computed(&Collider::ball(0.5), 1.0))); +/// ``` +/// +/// If you want, you can also define the mass properties explicitly by adding the components manually. +/// Note that the mass properties of colliders are added on top of the existing mass properties, so if you +/// want to define the body's mass properties explicitly, you might want to add +/// [`ColliderMassProperties::ZERO`](ColliderMassProperties#associatedconstant.ZERO) to the colliders. #[derive(Reflect, Default, Clone, Copy, Component, PartialEq, Eq)] #[reflect(Component)] pub enum RigidBody { From 8645ea84410e6673839c2f12b7b280260c7a63fa Mon Sep 17 00:00:00 2001 From: Jondolf Date: Sun, 18 Jun 2023 18:38:28 +0300 Subject: [PATCH 05/29] Update `RigidBody` docs --- src/components/mod.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/mod.rs b/src/components/mod.rs index 9fddb7a0..82773b0c 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -28,23 +28,23 @@ use derive_more::From; /// /// Creating a rigid body is as simple as adding the [`RigidBody`] component: /// -/// ``` +/// ```ignore /// commands.spawn(RigidBody::Dynamic); /// ``` /// /// Bevy XPBD will automatically add any missing components, like the following: /// -/// - [`Pos`] -/// - [`Rot`] -/// - [`LinVel`] -/// - [`AngVel`] +/// - [`Position`] +/// - [`Rotation`] +/// - [`LinearVelocity`] +/// - [`AngularVelocity`] /// - [`ExternalForce`] /// - [`ExternalTorque`] /// - [`Friction`] /// - [`Restitution`] /// - [`Mass`] /// - [`Inertia`] -/// - [`LocalCom`] +/// - [`CenterOfMass`] /// /// You can change any of these during initialization and runtime in order to alter the behaviour of the body. /// @@ -56,7 +56,7 @@ use derive_more::From; /// You should always give dynamic rigid bodies mass properties. The easiest way to do this is to [add a collider](collider), since colliders /// by default have [their own mass properties](ColliderMassProperties) that are added to the body's own mass properties. /// -/// ``` +/// ```ignore /// // The mass properties will be computed from a ball shape with a radius of 0.5 and a density of 1. /// commands.spawn((RigidBody::Dynamic, Collider::ball(0.5))); /// ``` @@ -64,9 +64,9 @@ use derive_more::From; /// If you don't want to add a collider, you can instead add a [`MassPropsBundle`] with the mass properties computed from a collider /// shape using the [`MassPropsBundle::new_computed`](MassPropsBundle#method.new_computed) method. /// -/// ``` +/// ```ignore /// // This is equivalent to the earlier approach, but no collider will be added. -/// commands.spawn((RigidBody::Dynamic, MassPropsBundle::new_computed(&Collider::ball(0.5), 1.0))); +/// commands.spawn((RigidBody::Dynamic, MassPropertiesBundle::new_computed(&Collider::ball(0.5), 1.0))); /// ``` /// /// If you want, you can also define the mass properties explicitly by adding the components manually. From 194483a2b44d289563ec481dfbc150024286bd9f Mon Sep 17 00:00:00 2001 From: Jondolf Date: Sun, 18 Jun 2023 18:42:08 +0300 Subject: [PATCH 06/29] Update crate-level docs --- src/lib.rs | 65 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 13 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e93e208f..34778ceb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,23 +3,34 @@ //! **Bevy XPBD** is a 2D and 3D physics engine based on [*Extended Position Based Dynamics* (XPBD)](#What_is_XPBD?) //! for the [Bevy game engine](https://bevyengine.org/). //! +//! ## Design +//! +//! Below are some of the core design principles used in Bevy XPBD. +//! +//! - Made with Bevy, for Bevy. +//! - Use the ECS as much as possible. A Bevy physics engine shouldn't need to maintain a separate physics world. +//! - Provide an ergonomic and familiar API. Creating and using [rigid bodies](RigidBody) and [colliders](Collider) shouldn't be hard. +//! - Use a highly modular [plugin architecture](plugins). Many large projects require custom-made solutions, so users should be able to +//! replace parts of the engine with their own implementations. +//! //! ## Features //! //! Below are some of the features of Bevy XPBD. //! -//! - 2D and 3D support with `bevy_xpbd_2d` and `bevy_xpbd_3d` //! - Dynamic, kinematic and static [rigid bodies](RigidBody) -//! - [Gravity](Gravity) -//! - [External forces](ExternalForce) and [torque](ExternalTorque) //! - [Colliders](Collider) powered by [parry](parry) +//! - Collision events: [`Collision`], [`CollisionStarted`], [`CollisionEnded`] +//! - Access to [colliding entities](CollidingEntities) +//! - [Sensor colliders](Sensor) +//! - [Collision layers](CollisionLayers) //! - Material properties like [restitution](Restitution) and [friction](Friction) -//! - Configurable [timesteps](PhysicsTimestep) -//! - Highly modular [plugin architecture](plugins) -//! - Choose number precision with the `f32`/`f64` feature (`f32` by default) -//! - Built-in [constraints] and support for [custom constraints](constraints#custom-constraints) +//! - External [forces](ExternalForce) and [torque](ExternalTorque) +//! - [Gravity](Gravity) //! - [Joints](joints) -//! - [Substepping](constraints#substepping) -//! - Automatically deactivating bodies with [sleeping](sleeping) to improve performance +//! - Built-in [constraints] and support for [custom constraints](constraints#custom-constraints) +//! - Automatically deactivating bodies with [sleeping](sleeping) +//! - Configurable [timesteps](PhysicsTimestep) and [substepping](SubstepCount) +//! - `f32`/`f64` precision (`f32` by default) //! //! ## Getting started //! @@ -58,6 +69,8 @@ //! - `f32` enables using `f32` numbers. Incompatible with `f64`. //! - `f64` enables using `f64` numbers. Recommended when encountering stability problems, especially with //! small timesteps. Incompatible with `f32`. +//! - `debug-plugin` enables the `PhysicsDebugPlugin` used for rendering physics objects and events like [AABBs](ColliderAabb) +//! and [contacts](Contact). //! - `simd` enables [SIMD](https://en.wikipedia.org/wiki/Single_instruction,_multiple_data) optimizations. //! - `enhanced-determinism` enables increased determinism. (Note: cross-platform determinism doesn't work yet, even //! with this feature enabled) @@ -68,7 +81,7 @@ //! manage different parts of the engine. These plugins can be easily initialized and configured through //! the [`PhysicsPlugins`] plugin group. //! -//! ``` +//! ```ignore //! use bevy::prelude::*; //! use bevy_xpbd_3d::prelude::*; //! @@ -84,7 +97,7 @@ //! For example, adding a [rigid body](RigidBody) with a [collider](Collider) is as simple as spawning an entity //! with the [`RigidBody`] and [`Collider`] components: //! -//! ``` +//! ```ignore //! commands.spawn((RigidBody::Dynamic, Collider::ball(0.5))); //! ``` //! @@ -106,14 +119,29 @@ //! cargo run --example cubes //! ``` //! +//! Note that 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. +//! +//! By default the examples use `f64`, so if you want to run the `f32` versions, you need to disable the default features and manually choose +//! the dimension and precision: +//! +//! ```bash +//! cargo run --example cubes --no-default-features --features 3d,f32 +//! ``` +//! //! ### Common tasks //! //! - [Create a rigid body](RigidBody) //! - [Add a collider](Collider) +//! - [Listen to collision events](Collider#collision-events) +//! - [Definen collision layers](CollisionLayers) //! - [Define mass properties](RigidBody#mass-properties) //! - [Use joints](joints) //! - [Configure gravity](Gravity) +//! - [Configure restitution](Restitution) +//! - [Configure friction](Friction) //! - [Configure the physics timestep](PhysicsTimestep) +//! - [Configure the substep count](SubstepCount) //! - [Create custom constraints](constraints) //! - [Replace built-in plugins with custom plugins](plugins#custom-plugins) //! @@ -133,7 +161,7 @@ //! ``` //! while simulating: //! // Substep size -//! h = ∆t / numSubsteps +//! h = ∆t / substep_count //! //! // Broad phase //! collect_collision_pairs() @@ -197,13 +225,24 @@ //! - Tutorial series: Johan Helsing. *[Tutorial: Making a physics engine with Bevy](https://johanhelsing.studio/posts/bevy-xpbd)*. //! (inspired this project) //! +//! License +//! +//! Bevy XPBD is free and open source. All code in this repository is dual-licensed under either: +//! +//! - MIT License ([LICENSE-MIT](https://github.com/Jondolf/bevy_xpbd/LICENSE-MIT) +//! or http://opensource.org/licenses/MIT) +//! - Apache License, Version 2.0 ([LICENSE-APACHE](https://github.com/Jondolf/bevy_xpbd/LICENSE-APACHE) +//! or http://www.apache.org/licenses/LICENSE-2.0) +//! +//! at your option. +//! //! [^1]: [`PhysicsSchedule`] //! //! [^2]: [`SubstepSchedule`] //! //! [^3]: [`PhysicsSet`] //! -//! [^4]: [`SubsteppingSet`] +//! [^4]: [`SubstepSet`] #![allow(rustdoc::invalid_rust_codeblocks)] #![warn(missing_docs)] From 793ef6ae5a175810d74ea4df4066652b341e44b8 Mon Sep 17 00:00:00 2001 From: Jondolf Date: Sun, 18 Jun 2023 19:25:31 +0300 Subject: [PATCH 07/29] Improve `constraints` docs --- src/constraints/mod.rs | 55 ++++++++++++++++++++++++++++++------------ src/lib.rs | 4 +-- 2 files changed, 41 insertions(+), 18 deletions(-) diff --git a/src/constraints/mod.rs b/src/constraints/mod.rs index 1b0cc268..76d8b38c 100644 --- a/src/constraints/mod.rs +++ b/src/constraints/mod.rs @@ -58,7 +58,7 @@ //! some of the background work for you. //! //! Add the `solve_constraint::` system to the -//! [substepping schedule's](SubstepSchedule) [`SubsteppingSet::SolveUserConstraints`] set. It should look like this: +//! [substepping schedule's](SubstepSchedule) [`SubstepSet::SolveUserConstraints`] set. It should look like this: //! //! ```ignore //! // Get substep schedule @@ -68,7 +68,7 @@ //! //! // Add custom constraint //! substeps.add_system( -//! solve_constraint::.in_set(SubsteppingSet::SolveUserConstraints), +//! solve_constraint::.in_set(SubstepSet::SolveUserConstraints), //! ); //! ``` //! @@ -80,9 +80,9 @@ //! In this section, you can learn some of the theory behind how constraints work. Understanding the theory and maths isn't //! important for using constraints, but it can be useful if you want to [create your own constraints](#custom-constraints). //! -//! **Note**: In the following theory, I primarily use the word "particle", but the same logic applies to normal +//! **Note**: In the following theory, primarily the word "particle" is used, but the same logic applies to normal //! [rigid bodies](RigidBody) as well. However, unlike particles, rigid bodies can also have angular quantities such as -//! [rotation](Rot) and [angular inertia](Inertia), so constraints can also affect their orientation. This is explained +//! [rotation](Rotation) and [angular inertia](Inertia), so constraints can also affect their orientation. This is explained //! in more detail [at the end](#rigid-body-constraints). //! //! ### Constraint functions @@ -102,9 +102,9 @@ //! //! ### Constraint gradients //! -//! To know what direction the constraint should move the particles, they use a *constraint gradient* `▽C(x)`. -//! It is a vector that point in the direction in which the constraint function value `C` increases the most. -//! The length of the gradient indicates how much `C` changes when moving a body by one unit. This is often equal to one. +//! To know what directions the particles should be moved towards, constraints compute a *constraint gradient* `▽C(x)` +//! for each particle. It is a vector that points in the direction in which the constraint function value `C` increases the most. +//! The length of the gradient indicates how much `C` changes when moving the particle by one unit. This is often equal to one. //! //! In a case where two particles are being constrained by a distance constraint, and the particles are outside of the //! rest distance, the gradient vector would point away from the other particle, because it would increase the distance @@ -142,24 +142,47 @@ //! Δx_i = Δλ * w_i * ▽C_i //! ``` //! -//! In other words, we typically move the particle along the gradient by `λ` proportional to the particle's inverse mass. +//! In other words, we typically move the particle along the gradient by `Δλ` proportional to the particle's inverse mass. //! //! ### Rigid body constraints //! -//! Unlike particles, [rigid bodies](RigidBody) also have angular quantities like [rotation](Rot), -//! [angular velocity](AngVel) and [angular inertia](Inertia). In addition, constraints can be applied at specific -//! points in the body, like contact positions or joint attachment positions, which also affects the orientation. +//! Unlike particles, [rigid bodies](RigidBody) also have angular quantities like [rotation](Rotation), +//! [angular velocity](AngularVelocity) and [angular inertia](Inertia). In addition, constraints can be applied at specific +//! points in the body, like [contact positions](Contact) or joint attachment positions, which also affects the orientation. //! -//! If the constraint function's value is the rotation angle and the gradient vector is the rotation axis, -//! we can compute the angular correction for a given body like this: +//! When the constraint is not applied at the center of mass, the inverse mass in the computation of `Δλ` must +//! be replaced with a *generalized inverse mass* that is essentially the effective mass when applying the constraint +//! at some specified position. +//! +//! For a positional constraint applied at position `r_i`, the generalized inverse mass computation for body `i` looks like this: +//! +//! ```text +//! w_i = 1 / m_i + (r_i x ▽C_i)^T * I_i^-1 * (r_i x ▽C_i) +//! ``` +//! +//! where `m_i` is the [mass](Mass) of body `i`, `I_i^-1` is the [inverse inertia tensor](InverseInertia), and `^T` refers to the +//! transpose of a vector. Note that the value of the inertia tensor depends on the orientation of the body, so it should be +//! recomputed each time the constraint is solved. +//! +//! For an angular constraint where the gradient vector is the rotation axis, the generalized inverse mass computation instead +//! looks like this: +//! +//! ```text +//! w_i = ▽C_i^T * I_i^-1 * ▽C_i +//! ``` +//! +//! Once we have computed the Lagrange multiplier update, we can apply the positional correction as shown in the +//! [previous section](#solving-constraints). +//! +//! However, angular constraints are handled differently. If the constraint function's value is the rotation angle and +//! the gradient vector is the rotation axis, we can compute the angular correction for a given body like this: //! //! ```text //! Δq_i = 0.5 * [I_i^-1 * (r_i x (Δλ * ▽C_i)), 0] * q_i //! ``` //! -//! where `q_i` is the rotation of body `i`, `I_i^-1` is the inverse inertia tensor, and `r_i` is a vector pointing -//! from the body's center of mass to some attachment position. Note that the value of the -//! inertia tensor depends on the orientation of the body, so it should be recomputed each time the constraint is solved. +//! where `q_i` is the [rotation](Rotation) of body `i` and `r_i` is a vector pointing from the body's center of mass to some +//! attachment position. pub mod joints; pub mod penetration; diff --git a/src/lib.rs b/src/lib.rs index 34778ceb..11094a49 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ //! # Bevy XPBD //! -//! **Bevy XPBD** is a 2D and 3D physics engine based on [*Extended Position Based Dynamics* (XPBD)](#What_is_XPBD?) +//! **Bevy XPBD** is a 2D and 3D physics engine based on [*Extended Position Based Dynamics* (XPBD)](#what-is-xpbd) //! for the [Bevy game engine](https://bevyengine.org/). //! //! ## Design @@ -142,7 +142,7 @@ //! - [Configure friction](Friction) //! - [Configure the physics timestep](PhysicsTimestep) //! - [Configure the substep count](SubstepCount) -//! - [Create custom constraints](constraints) +//! - [Create custom constraints](constraints#custom-constraints) //! - [Replace built-in plugins with custom plugins](plugins#custom-plugins) //! //! ## What is XPBD? From ecdcb7acfbe6e066b1b261b92f86fee15fa88a65 Mon Sep 17 00:00:00 2001 From: Jondolf Date: Sun, 18 Jun 2023 21:37:06 +0300 Subject: [PATCH 08/29] Improve `plugins` and system set docs --- src/lib.rs | 6 +-- src/plugins/mod.rs | 107 +++++++++++++++++++++++++++++++++++-------- src/plugins/setup.rs | 4 +- 3 files changed, 95 insertions(+), 22 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 11094a49..888f7b5a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -143,7 +143,7 @@ //! - [Configure the physics timestep](PhysicsTimestep) //! - [Configure the substep count](SubstepCount) //! - [Create custom constraints](constraints#custom-constraints) -//! - [Replace built-in plugins with custom plugins](plugins#custom-plugins) +//! - [Replace built-in plugins with custom plugins](PhysicsPlugins#custom-plugins) //! //! ## What is XPBD? //! @@ -201,13 +201,13 @@ //! //! In Bevy XPBD, the simulation loop is handled by various plugins. The [`PhysicsSetupPlugin`] sets up //! the Bevy schedules[^1][^2] and sets[^3][^4], the [`BroadPhasePlugin`] manages the broad phase, the [`IntegratorPlugin`] handles -//! XPBD integration, and so on. You can find all of the plugins and their responsibilities [here](plugins). +//! XPBD integration, and so on. You can find all of the plugins and their responsibilities [here](PhysicsPlugins). //! //! ### See also //! //! - [XPBD integration step](integrator) //! - [Constraints and how to create them](constraints) -//! - [Schedules and sets used for the simulation loop](PhysicsSetupPlugin) +//! - [Schedules and sets used for the simulation loop](PhysicsSetupPlugin#schedules-and-sets) //! //! ### Learning resources //! diff --git a/src/plugins/mod.rs b/src/plugins/mod.rs index 91d8e2e5..59072489 100644 --- a/src/plugins/mod.rs +++ b/src/plugins/mod.rs @@ -1,4 +1,23 @@ -//! The steps used in the simulation loop. +//! The building blocks of Bevy XPBD. +//! +//! Bevy XPBD consists of multiple different plugins, each with their own responsibilities. These plugins +//! are grouped into the [`PhysicsPlugins`] plugin group, which allows you to easily configure and +//! disable any of the existing plugins. +//! +//! This means that users can simply disable existing functionality and replace it +//! with specialized solutions, while keeping the rest of the engine's features. This can be important +//! in many games and applications that require special optimizations or features. +//! +//! Currently, the only plugin that is truly necessary is the [`PhysicsSetupPlugin`] that initializes the +//! engine's resources, schedules and sets, and even that could be implemented manually if you wanted +//! a custom scheduler for example. +//! +//! ## See also +//! +//! - [All of the current plugins and their responsibilities](PhysicsPlugins) +//! - [Creating custom plugins](PhysicsPlugins#custom-plugins) +//! - [`PhysicsSchedule`] and [`PhysicsSet`] +//! - [`SubstepSchedule`] and [`SubstepSet`] pub mod broad_phase; #[cfg(feature = "debug-plugin")] @@ -24,25 +43,77 @@ pub use sync::SyncPlugin; use crate::prelude::*; // For doc comments use bevy::prelude::*; -/// This plugin group will add the following physics plugins: +/// A plugin group containing all of Bevy XPBD's plugins. /// -/// - [`PhysicsSetupPlugin`] -/// - [`PreparePlugin`] -/// - [`BroadPhasePlugin`] -/// - [`IntegratorPlugin`] -/// - [`SolverPlugin`] -/// - [`SleepingPlugin`] -/// - [`SyncPlugin`] -/// - `PhysicsDebugPlugin` (with `debug-plugin` feature enabled) +/// By default, the following plugins will be added: /// -/// Note that the [`PhysicsSetupPlugin`] initializes all of the schedules, sets and resources required -/// by the other plugins, so it is necessary. +/// - [`PhysicsSetupPlugin`]: Sets up the physics engine by initializing the necessary schedules, sets and resources. +/// - [`PreparePlugin`]: Runs systems at the start of each physics frame; initializes [rigid bodies](RigidBody) +/// and [colliders](Collider) and updates components. +/// - [`BroadPhasePlugin`]: Collects pairs of potentially colliding entities into [`BroadCollisionPairs`] using +/// [AABB](ColliderAabb) intersection checks. +/// - [`IntegratorPlugin`]: Integrates Newton's 2nd law of motion, applying forces and moving entities according to their velocities. +/// - [`SolverPlugin`]: Solves positional and angular [constraints], updates velocities and solves velocity constraints +/// (dynamic [friction](Friction) and [restitution](Restitution)). +/// - [`SleepingPlugin`]: Controls when bodies should be deactivated and marked as [`Sleeping`] to improve performance. +/// - [`SyncPlugin`]: Synchronizes the engine's [`Position`] and [`Rotation`] with Bevy's [`Transform`]s. +/// - `PhysicsDebugPlugin`: Renders physics objects and events like [AABBs](ColliderAabb) and [contacts](Collision) +/// for debugging purposes (only with `debug-plugin` feature enabled). /// -/// Other than that, you can disable and configure the plugins freely, and even plug in your own implementations. -/// For example, you could replace the broad phase with your own specialized solution, or create your own sync plugin -/// that synchronizes the state of the physics world to something else than Bevy's transforms. +/// Refer to the documentation of the plugins for more information about their responsibilities and implementations. /// -/// Refer to the documentation of the other plugins for more detailed information about the default implementations. +/// You can also find more information regarding the engine's general plugin architecture [here](plugins). +/// +/// ## Custom plugins +/// +/// First, create a new plugin. If you want to run your systems in the engine's schedules, get either the [`PhysicsSchedule`] +/// or the [`SubstepSchedule`]. Then you can add your systems to that schedule and control system ordering with +/// [`PhysicsSet`] or [`SubstepSet`]. +/// +/// Here we will create a custom broad phase plugin that will replace the default [`BroadPhasePlugin`]: +/// +/// ```ignore +/// pub struct CustomBroadPhasePlugin; +/// +/// impl Plugin for CustomBroadPhasePlugin { +/// fn build(&self, app: &mut App) { +/// // Make sure the PhysicsSchedule is available +/// let physics_schedule = app +/// .get_schedule_mut(PhysicsSchedule) +/// .expect("add PhysicsSchedule first"); +/// +/// // Add the system into the broad phase system set +/// physics_schedule.add_system(collect_collision_pairs.in_set(PhysicsSet::BroadPhase)); +/// } +/// } +/// +/// fn collect_collision_pairs() { +/// // Implementation goes here +/// } +/// ``` +/// +/// Next, when creating your app, simply disable the default [`BroadPhasePlugin`] and add your custom plugin: +/// +/// ```ignore +/// fn main() { +/// let mut app = App::new(); +/// +/// app.add_plugins(DefaultPlugins); +/// +/// // Add PhysicsPlugins and replace default broad phase with our custom broad phase +/// app.add_plugins( +/// PhysicsPlugins +/// .build() +/// .disable::() +/// .add(CustomBroadPhasePlugin), +/// ); +/// +/// app.run(); +/// } +/// ``` +/// +/// You can find a full working example +/// [here](https://github.com/Jondolf/bevy_xpbd/blob/main/crates/bevy_xpbd_3d/examples/custom_broad_phase.rs). pub struct PhysicsPlugins; impl PluginGroup for PhysicsPlugins { @@ -66,7 +137,7 @@ impl PluginGroup for PhysicsPlugins { } } -/// The main steps in the physics simulation loop. +/// System sets for the main steps in the physics simulation loop. These are typically run in the [`PhysicsSchedule`]. /// /// 1. Prepare /// 2. Broad phase @@ -93,7 +164,7 @@ pub enum PhysicsSet { Sync, } -/// The steps in the inner substepping loop. +/// System sets for the the steps in the inner substepping loop. These are typically run in the [`SubstepSchedule`]. /// /// 1. Integrate /// 2. Solve positional and angular constraints diff --git a/src/plugins/setup.rs b/src/plugins/setup.rs index 9b2b7712..13d84e34 100644 --- a/src/plugins/setup.rs +++ b/src/plugins/setup.rs @@ -5,7 +5,7 @@ use crate::prelude::*; /// Sets up the physics engine by initializing the necessary schedules, sets and resources. /// /// This plugin does *not* initialize any other plugins or physics systems. -/// For that, add the plugins in [`PhysicsPlugins`], or even create your own plugins using +/// For that, add the plugins in [`PhysicsPlugins`], or even [create your own plugins](PhysicsPlugins#custom-plugins) using /// the schedules and sets provided by this setup plugin. /// /// ## Schedules and sets @@ -118,11 +118,13 @@ impl Plugin for PhysicsSetupPlugin { } /// The high-level physics schedule that runs once per physics frame. +/// See [`PhysicsSet`] for the system sets that are run in this schedule. #[derive(Debug, Hash, PartialEq, Eq, Clone, ScheduleLabel)] pub struct PhysicsSchedule; /// The substepping schedule. The number of substeps per physics step is /// configured through the [`SubstepCount`] resource. +/// See [`SubstepSet`] for the system sets that are run in this schedule. #[derive(Debug, Hash, PartialEq, Eq, Clone, ScheduleLabel)] pub struct SubstepSchedule; From 3265b8d18ec3180ac245b8ad0649045cf8c8a8f3 Mon Sep 17 00:00:00 2001 From: Jondolf Date: Sun, 18 Jun 2023 22:34:26 +0300 Subject: [PATCH 09/29] Improve `Collider` docs --- src/components/collider.rs | 49 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/components/collider.rs b/src/components/collider.rs index abadf927..8b7f573b 100644 --- a/src/components/collider.rs +++ b/src/components/collider.rs @@ -6,6 +6,55 @@ use derive_more::From; use parry::{bounding_volume::Aabb, shape::SharedShape}; /// A collider used for collision detection. +/// +/// By default, colliders generate [collision events](#collision-events) and cause a collision response for +/// [rigid bodies](RigidBody). If you only want collision events, you can add a [`Sensor`] component. +/// +/// ## Creation +/// +/// `Collider` has tons of methods for creating colliders of various shapes. +/// For example, to add a ball collider to a [rigid body](RigidBody), use [`Collider::ball`](#method.ball): +/// +/// ```ignore +/// command.spawn((RigidBody::Dynamic, Collider::ball(0.5))); +/// ``` +/// +/// In addition, Bevy XPBD will automatically add some other components, like the following: +/// +/// - [`ColliderAabb`] +/// - [`CollidingEntities`] +/// - [`ColliderMassProperties`] +/// +/// ## Collision layers +/// +/// You can use collsion layers to configure which entities can collide with each other. +/// +/// See [`CollisionLayers`] for more information and examples. +/// +/// ## Collision events +/// +/// There are currently three different collision events: [`Collision`], [`CollisionStarted`] and [`CollisionEnded`]. +/// You can listen to these events as you normally would. +/// +/// For example, you could read [contacts](Contact) like this: +/// +/// ```ignore +/// use bevy::prelude::*; +/// use bevy_xpbd_3d::prelude::*; +/// +/// fn my_system(mut collision_event_reader: EventReader) { +/// for Collision(contact) in collision_event_reader.iter() { +/// println!("{} and {} are colliding", contact.entity1, contact.entity2); +/// } +/// } +/// ``` +/// +/// ## Advanced usage +/// +/// Internally, `Collider` uses the shapes provided by `parry`. If you want to create a collider +/// using these shapes, you can simply use `Collider::from(SharedShape::some_method())`. +/// +/// To get a reference to the internal [`SharedShape`], you can use the [`get_shape`](#method.get_shape) method. #[derive(Clone, Component, Deref, DerefMut, From)] pub struct Collider(SharedShape); From 36b0da3b501d452951e1488d03fd6ee07f6010a4 Mon Sep 17 00:00:00 2001 From: Jondolf Date: Sun, 18 Jun 2023 22:35:57 +0300 Subject: [PATCH 10/29] Change `CollisionLayers` docs creation section title --- src/components/layers.rs | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/layers.rs b/src/components/layers.rs index 834bcb40..23816b61 100644 --- a/src/components/layers.rs +++ b/src/components/layers.rs @@ -32,7 +32,7 @@ impl PhysicsLayer for &L { /// Colliders without this component can be considered as having all groups and masks, and they can /// interact with everything that belongs on any layer. /// -/// ## Creating [`CollisionLayers`] +/// ## Creation /// /// The easiest way to build a [`CollisionLayers`] configuration is to use the [`CollisionLayers::new()`](#method.new) method /// that takes in a list of groups and masks. Additional groups and masks can be added and removed by calling methods like diff --git a/src/lib.rs b/src/lib.rs index 888f7b5a..8a4dd375 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -134,7 +134,7 @@ //! - [Create a rigid body](RigidBody) //! - [Add a collider](Collider) //! - [Listen to collision events](Collider#collision-events) -//! - [Definen collision layers](CollisionLayers) +//! - [Define collision layers](CollisionLayers#creation) //! - [Define mass properties](RigidBody#mass-properties) //! - [Use joints](joints) //! - [Configure gravity](Gravity) From 2d8f6874b3d8346ebe16b0ff748d9687d85c43ad Mon Sep 17 00:00:00 2001 From: Jondolf Date: Sun, 18 Jun 2023 22:36:50 +0300 Subject: [PATCH 11/29] Improve `Restitution` and `Friction` docs and add friction coefficient helpers --- src/components/mod.rs | 102 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 92 insertions(+), 10 deletions(-) diff --git a/src/components/mod.rs b/src/components/mod.rs index 82773b0c..dcfa7997 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -279,11 +279,38 @@ pub enum CoefficientCombine { Max = 4, } -/// Restitution controls how elastic or bouncy a body is. +/// Controls how elastic or bouncy an entity is when colliding with other entities. /// -/// 0.0: perfectly inelastic\ -/// 1.0: perfectly elastic\ -/// 2.0: kinetic energy is doubled +/// 0.0: Perfectly inelastic\ +/// 1.0: Perfectly elastic\ +/// 2.0: Kinetic energy is doubled +/// +/// ## Example +/// +/// Create a new [`Restitution`] component with a restitution coefficient of 0.4: +/// +/// ```ignore +/// Restitution::new(0.4) +/// ``` +/// +/// Configure how two restitution coefficients are combined with [`CoefficientCombine`]: +/// +/// ```ignore +/// Restitution::new(0.4).with_combine_rule(CoefficientCombine::Multiply) +/// ``` +/// +/// Combine the properties of two [`Restitution`] components: +/// +/// ```ignore +/// let first = Restitution::new(0.8).with_combine_rule(CoefficientCombine::Average); +/// let second = Restitution::new(0.5).with_combine_rule(CoefficientCombine::Multiply); +/// +/// // CoefficientCombine::Multiply has higher priority, so the coefficients are multiplied +/// assert_eq!( +/// first.combine(second), +/// Restitution::new(0.4).with_combine_rule(CoefficientCombine::Multiply) +/// ); +/// ``` #[derive(Reflect, Clone, Copy, Component, Debug, PartialEq, PartialOrd)] #[reflect(Component)] pub struct Restitution { @@ -294,12 +321,13 @@ pub struct Restitution { } impl Restitution { + /// Zero restitution and [`CoefficientCombine::Average`]. pub const ZERO: Self = Self { coefficient: 0.0, combine_rule: CoefficientCombine::Average, }; - /// Creates a new `Restitution` component with the given restitution coefficient. + /// Creates a new [`Restitution`] component with the given restitution coefficient. pub fn new(coefficient: Scalar) -> Self { Self { coefficient, @@ -315,7 +343,7 @@ impl Restitution { } } - /// Combines the properties of two `Restitution` components. + /// Combines the properties of two [`Restitution`] components. pub fn combine(&self, other: Self) -> Self { // Choose rule with higher priority let rule = self.combine_rule.max(other.combine_rule); @@ -350,12 +378,49 @@ impl From for Restitution { } } -/// Friction prevents relative tangential movement at contact points. +/// Controls how strongly the material of an entity prevents relative tangential movement at contact points. +/// +/// For surfaces that are at rest relative to each other, static friction is used. +/// Once the static friction is overcome, the bodies will start sliding relative to each other, and dynamic friction is applied instead. +/// +/// 0.0: No friction at all, the body slides indefinitely\ +/// 1.0: High friction\ +/// +/// ## Example +/// +/// Create a new [`Friction`] component with dynamic and static friction coefficients of 0.4: +/// +/// ```ignore +/// Friction::new(0.4) +/// ``` +/// +/// Set the other friction coefficient: /// -/// For surfaces that are at rest relative to each other, static friction is used. Once it is overcome, the bodies start sliding relative to each other, and dynamic friction is applied instead. +/// ```ignore +/// // 0.4 static and 0.6 dynamic +/// Friction::new(0.4).with_dynamic_coefficient(0.6) +/// // 0.4 dynamic and 0.6 static +/// Friction::new(0.4).with_static_coefficient(0.6) +/// ``` +/// +/// Configure how the friction coefficients of two [`Friction`] components are combined with [`CoefficientCombine`]: +/// +/// ```ignore +/// Friction::new(0.4).with_combine_rule(CoefficientCombine::Multiply) +/// ``` /// -/// 0.0: no friction at all, the body slides indefinitely\ -/// 1.0: high friction\ +/// Combine the properties of two [`Friction`] components: +/// +/// ```ignore +/// let first = Friction::new(0.8).with_combine_rule(CoefficientCombine::Average); +/// let second = Friction::new(0.5).with_combine_rule(CoefficientCombine::Multiply); +/// +/// // CoefficientCombine::Multiply has higher priority, so the coefficients are multiplied +/// assert_eq!( +/// first.combine(second), +/// Friction::new(0.4).with_combine_rule(CoefficientCombine::Multiply) +/// ); +/// ``` #[derive(Reflect, Clone, Copy, Component, Debug, PartialEq, PartialOrd)] #[reflect(Component)] pub struct Friction { @@ -368,6 +433,7 @@ pub struct Friction { } impl Friction { + /// Zero dynamic and static friction and [`CoefficientCombine::Average`]. pub const ZERO: Self = Self { dynamic_coefficient: 0.0, static_coefficient: 0.0, @@ -391,6 +457,22 @@ impl Friction { } } + /// Sets the coefficient of dynamic friction. + pub fn with_dynamic_coefficient(&self, coefficient: Scalar) -> Self { + Self { + dynamic_coefficient: coefficient, + ..*self + } + } + + /// Sets the coefficient of static friction. + pub fn with_static_coefficient(&self, coefficient: Scalar) -> Self { + Self { + static_coefficient: coefficient, + ..*self + } + } + /// Combines the properties of two `Friction` components. pub fn combine(&self, other: Self) -> Self { // Choose rule with higher priority From 8e7453043e20c12bed442fcd6db19308eed256ed Mon Sep 17 00:00:00 2001 From: Jondolf Date: Sun, 18 Jun 2023 22:47:58 +0300 Subject: [PATCH 12/29] Fix links in docs --- src/components/collider.rs | 2 +- src/components/mod.rs | 6 +++--- src/constraints/mod.rs | 6 ++++-- src/lib.rs | 8 ++++---- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/components/collider.rs b/src/components/collider.rs index 8b7f573b..9c5679ae 100644 --- a/src/components/collider.rs +++ b/src/components/collider.rs @@ -305,7 +305,7 @@ pub struct Sensor; pub struct ColliderAabb(pub Aabb); impl ColliderAabb { - /// Creates a new collider from a given [`Shape`] with a default density of 1.0. + /// Creates a new collider from a given [`SharedShape`] with a default density of 1.0. pub fn from_shape(shape: &SharedShape) -> Self { Self(shape.compute_local_aabb()) } diff --git a/src/components/mod.rs b/src/components/mod.rs index dcfa7997..f0da7e8b 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -53,7 +53,7 @@ use derive_more::From; /// /// ## Adding mass properties /// -/// You should always give dynamic rigid bodies mass properties. The easiest way to do this is to [add a collider](collider), since colliders +/// You should always give dynamic rigid bodies mass properties. The easiest way to do this is to [add a collider](Collider), since colliders /// by default have [their own mass properties](ColliderMassProperties) that are added to the body's own mass properties. /// /// ```ignore @@ -61,8 +61,8 @@ use derive_more::From; /// commands.spawn((RigidBody::Dynamic, Collider::ball(0.5))); /// ``` /// -/// If you don't want to add a collider, you can instead add a [`MassPropsBundle`] with the mass properties computed from a collider -/// shape using the [`MassPropsBundle::new_computed`](MassPropsBundle#method.new_computed) method. +/// If you don't want to add a collider, you can instead add a [`MassPropertiesBundle`] with the mass properties computed from a collider +/// shape using the [`MassPropertiesBundle::new_computed`](MassPropertiesBundle#method.new_computed) method. /// /// ```ignore /// // This is equivalent to the earlier approach, but no collider will be added. diff --git a/src/constraints/mod.rs b/src/constraints/mod.rs index 76d8b38c..e2ca79cd 100644 --- a/src/constraints/mod.rs +++ b/src/constraints/mod.rs @@ -3,7 +3,7 @@ //! # Constraints //! //! **Constraints** are a way to model physical relationships between entities. They are an integral part of XPBD, and they can be used -//! for things like [contact resolution](penetration::PenetrationConstraint), [joints], soft bodies, and much more. +//! for things like [contact resolution](PenetrationConstraint), [joints], soft bodies, and much more. //! //! At its core, a constraint is just a rule that is enforced by moving the participating entities in a way that satisfies that rule. //! For example, a distance constraint is satisfied when the distance between two entities is equal to the desired distance. @@ -14,7 +14,7 @@ //! //! Below are the currently implemented constraints. //! -//! - [`PenetrationConstraint`](penetration::PenetrationConstraint) +//! - [`PenetrationConstraint`](PenetrationConstraint) //! - [Joints](joints) //! - [`FixedJoint`] //! - [`SphericalJoint`] @@ -191,6 +191,8 @@ mod angular_constraint; mod position_constraint; pub use angular_constraint::AngularConstraint; +pub use joints::*; +pub use penetration::*; pub use position_constraint::PositionConstraint; use crate::prelude::*; diff --git a/src/lib.rs b/src/lib.rs index 8a4dd375..7b5b237f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -195,8 +195,8 @@ //! solve_velocities(particles and bodies) //! ``` //! -//! where `h` is the [substep size](SubDeltaTime), `q` is the [rotation](Rot) as a quaternion, -//! `ω` is the [angular velocity](AngVel), `I` is the [angular inertia tensor](`Inertia`) and `τ` is the +//! where `h` is the [substep size](SubDeltaTime), `q` is the [rotation](Rotation) as a quaternion, +//! `ω` is the [angular velocity](AngularVelocity), `I` is the [angular inertia tensor](`Inertia`) and `τ` is the //! [external torque](ExternalTorque). //! //! In Bevy XPBD, the simulation loop is handled by various plugins. The [`PhysicsSetupPlugin`] sets up @@ -230,9 +230,9 @@ //! Bevy XPBD is free and open source. All code in this repository is dual-licensed under either: //! //! - MIT License ([LICENSE-MIT](https://github.com/Jondolf/bevy_xpbd/LICENSE-MIT) -//! or http://opensource.org/licenses/MIT) +//! or ) //! - Apache License, Version 2.0 ([LICENSE-APACHE](https://github.com/Jondolf/bevy_xpbd/LICENSE-APACHE) -//! or http://www.apache.org/licenses/LICENSE-2.0) +//! or ) //! //! at your option. //! From b93be8afa5fd9e10f3b4ff0f7599954806a36529 Mon Sep 17 00:00:00 2001 From: Jondolf Date: Sun, 18 Jun 2023 23:34:48 +0300 Subject: [PATCH 13/29] Add troubleshooting section --- src/lib.rs | 53 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 7b5b237f..24984c22 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -135,7 +135,7 @@ //! - [Add a collider](Collider) //! - [Listen to collision events](Collider#collision-events) //! - [Define collision layers](CollisionLayers#creation) -//! - [Define mass properties](RigidBody#mass-properties) +//! - [Define mass properties](RigidBody#adding-mass-properties) //! - [Use joints](joints) //! - [Configure gravity](Gravity) //! - [Configure restitution](Restitution) @@ -145,6 +145,51 @@ //! - [Create custom constraints](constraints#custom-constraints) //! - [Replace built-in plugins with custom plugins](PhysicsPlugins#custom-plugins) //! +//! ## Troubleshooting +//! +//! ### Nothing is happening! +//! +//! Make sure you have added the [`PhysicsPlugins`] plugin group and you have given your rigid bodies +//! a [`RigidBody`] component. See the [getting started](#getting-started) section. +//! +//! ### Why is everything moving so slowly? +//! +//! If your application is in 2D, you might be using pixels as length units. This will require you to use +//! larger velocities and forces than you would in 3D. Make sure you set [`Gravity`] to some larger value +//! as well, because its magnitude is 9.81 by default, which is tiny in pixels. +//! +//! Bevy XPBD doesn't have a "physics scale" yet, but it will most likely be added in the future +//! so that it's possible to define some kind of pixels per meter configuration. +//! +//! ### Why did my rigid body suddenly vanish? +//! +//! Make sure to [give your rigid bodies some mass](RigidBody#adding-mass-properties), either by adding a [`Collider`] +//! or a [`MassPropertiesBundle`]. If your bodies don't have any mass, any physical interaction is likely to +//! instantly give them infinite velocity. +//! +//! ### Why is performance so bad? +//! +//! Make sure you are building your project in release mode using `cargo build --release`. +//! +//! You can further optimize builds by setting the number of codegen units in your `Cargo.toml` to 1, +//! although this will also increase compile times. +//! +//! ```toml +//! [profile.release] +//! codegen-units = 1 +//! ``` +//! +//! Note that Bevy XPBD simply isn't very optimized yet, and it mostly runs on a single thread for now. +//! This will be addressed in future releases. +//! +//! ### Something else? +//! +//! Physics engines are very large and Bevy XPBD is very young, so stability issues and bugs are to be expected. +//! +//! If you encounter issues, please consider first taking a look at the +//! [issues on GitHub](https://github.com/Jondolf/bevy_xpbd/issues) and +//! [open a new issue](https://github.com/Jondolf/bevy_xpbd/issues/new) if there already isn't one regarding your problem. +//! //! ## What is XPBD? //! //! *XPBD* or *Extended Position Based Dynamics* is a physics simulation method that extends @@ -225,9 +270,9 @@ //! - Tutorial series: Johan Helsing. *[Tutorial: Making a physics engine with Bevy](https://johanhelsing.studio/posts/bevy-xpbd)*. //! (inspired this project) //! -//! License +//! ## License //! -//! Bevy XPBD is free and open source. All code in this repository is dual-licensed under either: +//! Bevy XPBD is free and open source. All code in the Bevy XPBD repository is dual-licensed under either: //! //! - MIT License ([LICENSE-MIT](https://github.com/Jondolf/bevy_xpbd/LICENSE-MIT) //! or ) @@ -272,7 +317,7 @@ pub mod math; pub mod plugins; pub mod resources; -/// Reimports common components, bundles, resources, plugins and types. +/// Re-exports common components, bundles, resources, plugins and types. pub mod prelude { pub use crate::{ collision::*, From cf599ba1bfcaa684856ff74617f02ee1cb5badb9 Mon Sep 17 00:00:00 2001 From: Jondolf Date: Mon, 19 Jun 2023 11:50:39 +0300 Subject: [PATCH 14/29] Improve `joints` docs --- src/constraints/joints/mod.rs | 86 ++++++++++++++++++++++++++++++++++- src/lib.rs | 4 +- 2 files changed, 86 insertions(+), 4 deletions(-) diff --git a/src/constraints/joints/mod.rs b/src/constraints/joints/mod.rs index 96aecfcf..ca2bd0e6 100644 --- a/src/constraints/joints/mod.rs +++ b/src/constraints/joints/mod.rs @@ -1,4 +1,86 @@ -//! General joint logic and different types of built-in joints. +//! Contains joints. +//! +//! **Joints** are a way to connect entities in a way that restricts their movement relative to each other. +//! They act as [constraints] that restrict different *Degrees Of Freedom* depending on the joint type. +//! +//! ## Degrees Of Freedom (DOF) +//! +//! In 3D, entities can normally translate and rotate along the `X`, `Y` and `Z` axes. +//! Therefore, they have 3 translational DOF and 3 rotational DOF, which is a total of 6 DOF. +//! +//! Joints reduce the number of DOF that entities have. For example, [revolute joints](RevoluteJoint) +//! only allow rotation around one axis. +//! +//! Below is a table containing the joints that are currently implemented. +//! +//! | Joint | Allowed 2D DOF | Allowed 3D DOF | +//! | ------------------ | -------------- | -------------- | +//! | [`FixedJoint`] | None | None | +//! | [`PrismaticJoint`] | 1 Translation | 1 Translation | +//! | [`RevoluteJoint`] | 1 Rotation | 1 Rotation | +//! | [`SphericalJoint`] | 1 Rotation | 3 Rotations | +//! +//! ## Using joints +//! +//! In Bevy XPBD, joints are modeled as components. You can create a joint by simply spawning +//! an entity and adding the joint component you want, giving the connected entities as arguments +//! to the `new` method. +//! +//! ``` +//! # use bevy::prelude::*; +//! # #[cfg(feature = "2d")] +//! # use bevy_xpbd_2d::prelude::*; +//! # #[cfg(feature = "3d")] +//! # use bevy_xpbd_3d::prelude::*; +//! # +//! fn setup(mut commands: Commands) { +//! let entity1 = commands.spawn(RigidBody::Dynamic).id(); +//! let entity2 = commands.spawn(RigidBody::Dynamic).id(); +//! +//! // Connect the bodies with a fixed joint +//! commands.spawn(FixedJoint::new(entity1, entity2)); +//! } +//! ``` +//! +//! ### Stiffness +//! +//! You can control the stiffness of a joint with the `with_compliance` method. +//! *Compliance* refers to the inverse of stiffness, so using a compliance of 0 corresponds to +//! infinite stiffness. +//! +//! ### Attachment positions +//! +//! By default, joints are connected to the centers of entities, but attachment positions can be used to change this. +//! +//! You can use `with_local_anchor_1` and `with_local_anchor_2` to set the attachment positions on the first +//! and second entity respectively. +//! +//! ### Damping +//! +//! You can configure the linear and angular damping caused by joints using the `with_linear_velocity_damping` and +//! `with_angular_velocity_damping` methods. Increasing the damping values will cause the velocities +//! of the connected entities to decrease faster. +//! +//! ### Other configuration +//! +//! Different joints may have different configuration options. Many joints allow you to change the axis of allowed +//! translation or rotation, and they may have distance or angle limits along these axes. +//! +//! Take a look at the documentation and methods of each joint to see all of the configuration options. +//! +//! ## Custom joints +//! +//! Joints are [constraints] that implement [`Joint`] and [`XpbdConstraint`]. +//! +//! The process of creating a joint is essentially the same as [creating a constraint](constraints#custom-constraints), +//! except you should also implement the [`Joint`] trait's methods. The trait has some useful helper methods +//! like `align_position` and `align_orientation` to reduce some common boilerplate. +//! +//! Many joints also have joint limits. You can use [`DistanceLimit`] and [`AngleLimit`] to help store these limits +//! and to compute the current distance from the specified limits. +//! +//! [See the code implementations](https://github.com/Jondolf/bevy_xpbd/tree/main/src/constraints/joints) +//! of the implemented joints to get a better idea of how to create joints. mod fixed; mod prismatic; @@ -13,7 +95,7 @@ pub use spherical::*; use crate::prelude::*; use bevy::prelude::*; -/// Joints are constraints that attach pairs of bodies and restrict their relative positional and rotational degrees of freedom. +/// A trait for [joints]. pub trait Joint: Component + PositionConstraint + AngularConstraint { /// Creates a new joint between two entities. fn new(entity1: Entity, entity2: Entity) -> Self; diff --git a/src/lib.rs b/src/lib.rs index 24984c22..0a70dc6e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -136,7 +136,7 @@ //! - [Listen to collision events](Collider#collision-events) //! - [Define collision layers](CollisionLayers#creation) //! - [Define mass properties](RigidBody#adding-mass-properties) -//! - [Use joints](joints) +//! - [Use joints](joints#using-joints) //! - [Configure gravity](Gravity) //! - [Configure restitution](Restitution) //! - [Configure friction](Friction) @@ -172,7 +172,7 @@ //! Make sure you are building your project in release mode using `cargo build --release`. //! //! You can further optimize builds by setting the number of codegen units in your `Cargo.toml` to 1, -//! although this will also increase compile times. +//! although this will also increase build times. //! //! ```toml //! [profile.release] From 081de247f9c18a11f2a9f558a2d01e2a25ffaa72 Mon Sep 17 00:00:00 2001 From: Jondolf Date: Mon, 19 Jun 2023 12:23:29 +0300 Subject: [PATCH 15/29] Remove `ignore` from many doc examples --- src/components/collider.rs | 19 +++++++++++++++---- src/components/layers.rs | 7 +++++-- src/components/mod.rs | 26 ++++++++++++++++++++++---- src/components/world_queries.rs | 4 +++- src/constraints/mod.rs | 8 +++++++- src/lib.rs | 31 ++++++++++++++++++++++--------- src/plugins/mod.rs | 21 +++++++++++++++++++-- 7 files changed, 93 insertions(+), 23 deletions(-) diff --git a/src/components/collider.rs b/src/components/collider.rs index 9c5679ae..712f818c 100644 --- a/src/components/collider.rs +++ b/src/components/collider.rs @@ -15,8 +15,16 @@ use parry::{bounding_volume::Aabb, shape::SharedShape}; /// `Collider` has tons of methods for creating colliders of various shapes. /// For example, to add a ball collider to a [rigid body](RigidBody), use [`Collider::ball`](#method.ball): /// -/// ```ignore -/// command.spawn((RigidBody::Dynamic, Collider::ball(0.5))); +/// ``` +/// use bevy::prelude::*; +/// # #[cfg(feature = "2d")] +/// # use bevy_xpbd_2d::prelude::*; +/// # #[cfg(feature = "3d")] +/// use bevy_xpbd_3d::prelude::*; +/// +/// fn setup(mut commands: Commands) { +/// commands.spawn((RigidBody::Dynamic, Collider::ball(0.5))); +/// } /// ``` /// /// In addition, Bevy XPBD will automatically add some other components, like the following: @@ -38,13 +46,16 @@ use parry::{bounding_volume::Aabb, shape::SharedShape}; /// /// For example, you could read [contacts](Contact) like this: /// -/// ```ignore +/// ``` /// use bevy::prelude::*; +/// # #[cfg(feature = "2d")] +/// # use bevy_xpbd_2d::prelude::*; +/// # #[cfg(feature = "3d")] /// use bevy_xpbd_3d::prelude::*; /// /// fn my_system(mut collision_event_reader: EventReader) { /// for Collision(contact) in collision_event_reader.iter() { -/// println!("{} and {} are colliding", contact.entity1, contact.entity2); +/// println!("{:?} and {:?} are colliding", contact.entity1, contact.entity2); /// } /// } /// ``` diff --git a/src/components/layers.rs b/src/components/layers.rs index 23816b61..542bb98b 100644 --- a/src/components/layers.rs +++ b/src/components/layers.rs @@ -47,9 +47,12 @@ impl PhysicsLayer for &L { /// /// ## Example /// -/// ```ignore +/// ``` /// use bevy::prelude::*; -/// use bevy_xpbd::prelude::*; // 2D or 3D +/// # #[cfg(feature = "2d")] +/// # use bevy_xpbd_2d::prelude::*; +/// # #[cfg(feature = "3d")] +/// use bevy_xpbd_3d::prelude::*; /// /// #[derive(PhysicsLayer)] /// enum Layer { diff --git a/src/components/mod.rs b/src/components/mod.rs index f0da7e8b..72dea84c 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -28,8 +28,16 @@ use derive_more::From; /// /// Creating a rigid body is as simple as adding the [`RigidBody`] component: /// -/// ```ignore -/// commands.spawn(RigidBody::Dynamic); +/// ``` +/// use bevy::prelude::*; +/// # #[cfg(feature = "2d")] +/// # use bevy_xpbd_2d::prelude::*; +/// # #[cfg(feature = "3d")] +/// use bevy_xpbd_3d::prelude::*; +/// +/// fn setup(mut commands: Commands) { +/// commands.spawn(RigidBody::Dynamic); +/// } /// ``` /// /// Bevy XPBD will automatically add any missing components, like the following: @@ -301,7 +309,12 @@ pub enum CoefficientCombine { /// /// Combine the properties of two [`Restitution`] components: /// -/// ```ignore +/// ``` +/// # #[cfg(feature = "2d")] +/// # use bevy_xpbd_2d::prelude::*; +/// # #[cfg(feature = "3d")] +/// use bevy_xpbd_3d::prelude::*; +/// /// let first = Restitution::new(0.8).with_combine_rule(CoefficientCombine::Average); /// let second = Restitution::new(0.5).with_combine_rule(CoefficientCombine::Multiply); /// @@ -411,7 +424,12 @@ impl From for Restitution { /// /// Combine the properties of two [`Friction`] components: /// -/// ```ignore +/// ``` +/// # #[cfg(feature = "2d")] +/// # use bevy_xpbd_2d::prelude::*; +/// # #[cfg(feature = "3d")] +/// use bevy_xpbd_3d::prelude::*; +/// /// let first = Friction::new(0.8).with_combine_rule(CoefficientCombine::Average); /// let second = Friction::new(0.5).with_combine_rule(CoefficientCombine::Multiply); /// diff --git a/src/components/world_queries.rs b/src/components/world_queries.rs index 156e26a8..ffb46c06 100644 --- a/src/components/world_queries.rs +++ b/src/components/world_queries.rs @@ -1,3 +1,5 @@ +#![allow(missing_docs)] + use crate::prelude::*; use bevy::ecs::query::WorldQuery; use std::ops::{AddAssign, SubAssign}; @@ -32,7 +34,7 @@ impl<'w> RigidBodyQueryItem<'w> { *self.inverse_inertia } - // Computes the world-space inverse inertia tensor. + /// Computes the world-space inverse inertia tensor. #[cfg(feature = "3d")] pub fn world_inv_inertia(&self) -> InverseInertia { self.inverse_inertia.rotated(&self.rotation) diff --git a/src/constraints/mod.rs b/src/constraints/mod.rs index e2ca79cd..54cade5b 100644 --- a/src/constraints/mod.rs +++ b/src/constraints/mod.rs @@ -31,7 +31,13 @@ //! First, create a struct and implement the [`XpbdConstraint`] trait, giving the number of participating entities using generics. //! It should look similar to this: //! -//! ```ignore +//! ``` +//! use bevy::prelude::*; +//! # #[cfg(feature = "2d")] +//! # use bevy_xpbd_2d::prelude::*; +//! # #[cfg(feature = "3d")] +//! use bevy_xpbd_3d::prelude::*; +//! //! struct CustomConstraint { //! entity1: Entity, //! entity2: Entity, diff --git a/src/lib.rs b/src/lib.rs index 0a70dc6e..ab3286f1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -81,15 +81,20 @@ //! manage different parts of the engine. These plugins can be easily initialized and configured through //! the [`PhysicsPlugins`] plugin group. //! -//! ```ignore +//! ```no_run //! use bevy::prelude::*; +//! # #[cfg(feature = "2d")] +//! # use bevy_xpbd_2d::prelude::*; +//! # #[cfg(feature = "3d")] //! use bevy_xpbd_3d::prelude::*; //! -//! App::new() -//! .add_plugins(DefaultPlugins) -//! .add_plugins(PhysicsPlugins) -//! // ...your other plugins, systems and resources -//! .run(); +//! fn main() { +//! App::new() +//! .add_plugins(DefaultPlugins) +//! .add_plugins(PhysicsPlugins) +//! // ...your other plugins, systems and resources +//! .run(); +//! } //! ``` //! //! Now you can use all of Bevy XPBD's [components] and [resources] to build whatever you want! @@ -97,8 +102,16 @@ //! For example, adding a [rigid body](RigidBody) with a [collider](Collider) is as simple as spawning an entity //! with the [`RigidBody`] and [`Collider`] components: //! -//! ```ignore -//! commands.spawn((RigidBody::Dynamic, Collider::ball(0.5))); +//! ``` +//! # use bevy::prelude::*; +//! # #[cfg(feature = "2d")] +//! # use bevy_xpbd_2d::prelude::*; +//! # #[cfg(feature = "3d")] +//! # use bevy_xpbd_3d::prelude::*; +//! # +//! fn setup(mut commands: Commands) { +//! commands.spawn((RigidBody::Dynamic, Collider::ball(0.5))); +//! } //! ``` //! //! To learn more about using Bevy XPBD, consider taking a look at the official [examples](#examples) and @@ -203,7 +216,7 @@ //! //! Below is a high level overview of the XPBD simulation loop. //! -//! ``` +//! ```ignore //! while simulating: //! // Substep size //! h = ∆t / substep_count diff --git a/src/plugins/mod.rs b/src/plugins/mod.rs index 59072489..35189831 100644 --- a/src/plugins/mod.rs +++ b/src/plugins/mod.rs @@ -72,7 +72,13 @@ use bevy::prelude::*; /// /// Here we will create a custom broad phase plugin that will replace the default [`BroadPhasePlugin`]: /// -/// ```ignore +/// ``` +/// use bevy::prelude::*; +/// # #[cfg(feature = "2d")] +/// # use bevy_xpbd_2d::prelude::*; +/// # #[cfg(feature = "3d")] +/// use bevy_xpbd_3d::prelude::*; +/// /// pub struct CustomBroadPhasePlugin; /// /// impl Plugin for CustomBroadPhasePlugin { @@ -94,7 +100,18 @@ use bevy::prelude::*; /// /// Next, when creating your app, simply disable the default [`BroadPhasePlugin`] and add your custom plugin: /// -/// ```ignore +/// ```no_run +/// use bevy::prelude::*; +/// # #[cfg(feature = "2d")] +/// # use bevy_xpbd_2d::prelude::*; +/// # #[cfg(feature = "3d")] +/// use bevy_xpbd_3d::prelude::*; +/// +/// # struct CustomBroadPhasePlugin; +/// # impl Plugin for CustomBroadPhasePlugin { +/// # fn build(&self, app: &mut App) {} +/// # } +/// # /// fn main() { /// let mut app = App::new(); /// From ba935e41770c2035399fe73f990e4498ba007c86 Mon Sep 17 00:00:00 2001 From: Jondolf Date: Mon, 19 Jun 2023 12:35:28 +0300 Subject: [PATCH 16/29] Improve `Gravity` docs --- src/resources.rs | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/resources.rs b/src/resources.rs index 0ab9c98e..b1791001 100644 --- a/src/resources.rs +++ b/src/resources.rs @@ -99,9 +99,38 @@ impl Default for DeactivationTime { } } -/// The global gravitational acceleration. This is applied to dynamic bodies in the integration step. +/// A resource for the global gravitational acceleration. /// -/// The default is an acceleration of 9.81 m/s^2 pointing down, which is approximate to the gravitational acceleration near Earth's surface. +/// The default is an acceleration of 9.81 m/s^2 pointing down, which is approximate to the gravitational +/// acceleration near Earth's surface. +/// +/// Note that if you are using pixels as length units in 2D, this gravity will be tiny. You should +/// modify the gravity to fit your application. +/// +/// ## Example +/// +/// You can change gravity by simply inserting the [`Gravity`] resource: +/// +/// ```no_run +/// use bevy::prelude::*; +/// # #[cfg(feature = "2d")] +/// # use bevy_xpbd_2d::prelude::*; +/// # #[cfg(feature = "3d")] +/// use bevy_xpbd_3d::prelude::*; +/// +/// # #[cfg(all(feature = "3d", feature = "f32"))] +/// fn main() { +/// App::new() +/// .add_plugins(DefaultPlugins) +/// .add_plugins(PhysicsPlugins) +/// .insert_resource(Gravity(Vec3::NEG_Y * 19.6)) +/// .run(); +/// } +/// # #[cfg(not(all(feature = "3d", feature = "f32")))] +/// # fn main() {} +/// ``` +/// +/// You can also modify gravity while the app is running. #[derive(Reflect, Resource, Debug)] #[reflect(Resource)] pub struct Gravity(pub Vector); From a4200a77141d11928a3bda955017f3a2fa6d6a7e Mon Sep 17 00:00:00 2001 From: Jondolf Date: Mon, 19 Jun 2023 12:50:33 +0300 Subject: [PATCH 17/29] Improve timestep and substep count docs --- src/lib.rs | 2 +- src/resources.rs | 51 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ab3286f1..0e9bbecc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,7 +28,7 @@ //! - [Gravity](Gravity) //! - [Joints](joints) //! - Built-in [constraints] and support for [custom constraints](constraints#custom-constraints) -//! - Automatically deactivating bodies with [sleeping](sleeping) +//! - Automatically deactivating bodies with [sleeping](Sleeping) //! - Configurable [timesteps](PhysicsTimestep) and [substepping](SubstepCount) //! - `f32`/`f64` precision (`f32` by default) //! diff --git a/src/resources.rs b/src/resources.rs index b1791001..e140ab53 100644 --- a/src/resources.rs +++ b/src/resources.rs @@ -5,6 +5,27 @@ use bevy::prelude::Resource; use crate::prelude::*; /// Configures how many times per second the physics simulation is run. +/// +/// ## Example +/// +/// You can change the timestep by inserting the [`PhysicsTimestep`] resource: +/// +/// ```no_run +/// use bevy::prelude::*; +/// # #[cfg(feature = "2d")] +/// # use bevy_xpbd_2d::prelude::*; +/// # #[cfg(feature = "3d")] +/// use bevy_xpbd_3d::prelude::*; +/// +/// fn main() { +/// App::new() +/// .add_plugins(DefaultPlugins) +/// .add_plugins(PhysicsPlugins) +/// // Use a 120 Hz fixed timestep instead of the default 60 Hz +/// .insert_resource(PhysicsTimestep::Fixed(1.0 / 120.0)) +/// .run(); +/// } +/// ``` #[derive(Reflect, Resource, Clone, Copy, Debug, PartialEq)] #[reflect(Resource)] pub enum PhysicsTimestep { @@ -35,7 +56,35 @@ pub struct DeltaTime(pub Scalar); #[reflect(Resource)] pub struct SubDeltaTime(pub Scalar); -/// The number of substeps used in XPBD simulation. A higher number of substeps reduces the value of [`SubDeltaTime`], which results in a more accurate simulation at the cost of performance. +/// The number of substeps used in the simulation. +/// +/// A higher number of substeps reduces the value of [`SubDeltaTime`], +/// which results in a more accurate simulation, but also reduces performance. The default +/// substep count is currently 12. +/// +/// If you use a very high substep count and encounter stability issues, consider enabling the `f64` +/// feature as shown in the [getting started guide](crate#getting-started) to avoid floating point +/// precision problems. +/// +/// ## Example +/// +/// You can change the number of substeps by inserting the [`SubstepCount`] resource: +/// +/// ```no_run +/// use bevy::prelude::*; +/// # #[cfg(feature = "2d")] +/// # use bevy_xpbd_2d::prelude::*; +/// # #[cfg(feature = "3d")] +/// use bevy_xpbd_3d::prelude::*; +/// +/// fn main() { +/// App::new() +/// .add_plugins(DefaultPlugins) +/// .add_plugins(PhysicsPlugins) +/// .insert_resource(SubstepCount(30)) +/// .run(); +/// } +/// ``` #[derive(Reflect, Resource, Clone, Copy)] #[reflect(Resource)] pub struct SubstepCount(pub u32); From 195651eb0f4a987a8a3791bf486e5f6153491e03 Mon Sep 17 00:00:00 2001 From: Jondolf Date: Mon, 19 Jun 2023 12:52:48 +0300 Subject: [PATCH 18/29] Add doc link --- src/plugins/setup.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/setup.rs b/src/plugins/setup.rs index 13d84e34..ff379034 100644 --- a/src/plugins/setup.rs +++ b/src/plugins/setup.rs @@ -22,7 +22,7 @@ use crate::prelude::*; /// The [`SubstepSchedule`] handles physics substepping. It is run [`SubstepCount`] times in [`PhysicsSet::Substeps`], /// and it typically handles things like collision detection and constraint solving. /// -/// Substepping sets are added by the solver plugin if it is enabled. See [`SolverPlugin`] for more information. +/// [Substepping sets](SubstepSet) are added by the solver plugin if it is enabled. See [`SolverPlugin`] for more information. pub struct PhysicsSetupPlugin; impl Plugin for PhysicsSetupPlugin { From b35ccd65226cde53a96c5ebceec9d859010f7f84 Mon Sep 17 00:00:00 2001 From: Jondolf Date: Mon, 19 Jun 2023 13:28:07 +0300 Subject: [PATCH 19/29] Improve `Rotation` docs --- src/components/rotation.rs | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/components/rotation.rs b/src/components/rotation.rs index b210c711..9ed26b2d 100644 --- a/src/components/rotation.rs +++ b/src/components/rotation.rs @@ -11,16 +11,43 @@ use crate::prelude::*; /// The rotation of a body. /// -/// To speed up computation, the rotation is stored as the cosine and sine of the given angle in radians. You should use the associated methods to create, access and modify rotations with normal radians or degrees. +/// To speed up computation, the rotation is stored as the cosine and sine of the given angle in radians. +/// You should use the associated methods to create, access and modify rotations with normal radians or degrees. +/// +/// ## Example +/// +/// ``` +/// use bevy::prelude::*; +/// use bevy_xpbd_2d::prelude::*; +/// +/// fn setup(mut commands: Commands) { +/// // Spawn a dynamic rigid body rotated by 90 degrees +/// commands.spawn((RigidBody::Dynamic, Rotation::from_degrees(90))) +/// } +/// ``` #[cfg(feature = "2d")] #[derive(Reflect, Clone, Copy, Component, Debug)] #[reflect(Component)] pub struct Rotation { - pub cos: Scalar, - pub sin: Scalar, + /// The cosine of the rotation angle in radians. + cos: Scalar, + /// The sine of the rotation angle in radians. + sin: Scalar, } /// The rotation of a body represented as a [`Quat`]. +/// +/// ## Example +/// +/// ``` +/// use bevy::prelude::*; +/// use bevy_xpbd_2d::prelude::*; +/// +/// fn setup(mut commands: Commands) { +/// // Spawn a dynamic rigid body rotated by 1.5 radians around the x axis +/// commands.spawn((RigidBody::Dynamic, Rotation::from_rotation_x(1.5))) +/// } +/// ``` #[cfg(feature = "3d")] #[derive(Reflect, Clone, Copy, Component, Debug, Default, Deref, DerefMut)] #[reflect(Component)] @@ -45,6 +72,7 @@ impl Rotation { #[cfg(feature = "2d")] impl Rotation { + /// Zero rotation. pub const ZERO: Self = Self { cos: 1.0, sin: 0.0 }; /// Returns the cosine of the rotation in radians. From 5492927140cc04d06d879cae59c557813e0ff60c Mon Sep 17 00:00:00 2001 From: Jondolf Date: Mon, 19 Jun 2023 13:28:21 +0300 Subject: [PATCH 20/29] Add missing docs --- src/components/layers.rs | 2 ++ src/components/mass_properties.rs | 9 +++++++++ src/components/mod.rs | 3 +++ src/constraints/angular_constraint.rs | 9 +++++++++ src/constraints/joints/mod.rs | 4 ++++ src/constraints/penetration.rs | 1 + src/constraints/position_constraint.rs | 6 ++++++ src/math/double.rs | 8 ++++++++ src/math/mod.rs | 4 ++++ src/resources.rs | 1 + 10 files changed, 47 insertions(+) diff --git a/src/components/layers.rs b/src/components/layers.rs index 542bb98b..f1e70216 100644 --- a/src/components/layers.rs +++ b/src/components/layers.rs @@ -5,7 +5,9 @@ use bevy::prelude::*; /// /// This trait can be derived for enums with `#[derive(PhysicsLayer)]`. pub trait PhysicsLayer: Sized { + /// Converts the layer to a bitmask. fn to_bits(&self) -> u32; + /// Creates a layer bitmask with all bits set to 1. fn all_bits() -> u32; } diff --git a/src/components/mass_properties.rs b/src/components/mass_properties.rs index 5f0ea519..8ff79be7 100644 --- a/src/components/mass_properties.rs +++ b/src/components/mass_properties.rs @@ -10,6 +10,7 @@ use crate::utils::get_rotated_inertia_tensor; pub struct Mass(pub Scalar); impl Mass { + /// Zero mass. pub const ZERO: Self = Self(0.0); } @@ -19,6 +20,7 @@ impl Mass { pub struct InverseMass(pub Scalar); impl InverseMass { + /// Zero inverse mass. pub const ZERO: Self = Self(0.0); } @@ -47,8 +49,10 @@ impl Default for Inertia { } impl Inertia { + /// Zero angular inertia. #[cfg(feature = "2d")] pub const ZERO: Self = Self(0.0); + /// Zero angular inertia. #[cfg(feature = "3d")] pub const ZERO: Self = Self(Matrix3::ZERO); @@ -103,8 +107,10 @@ impl Default for InverseInertia { } impl InverseInertia { + /// Zero inverse angular inertia. #[cfg(feature = "2d")] pub const ZERO: Self = Self(0.0); + /// Zero inverse angular inertia. #[cfg(feature = "3d")] pub const ZERO: Self = Self(Matrix3::ZERO); @@ -145,9 +151,11 @@ impl From for InverseInertia { pub struct CenterOfMass(pub Vector); impl CenterOfMass { + /// A center of mass set at the local origin. pub const ZERO: Self = Self(Vector::ZERO); } +#[allow(missing_docs)] #[derive(Bundle, Debug, Default, Clone, PartialEq)] pub struct MassPropertiesBundle { pub mass: Mass, @@ -202,6 +210,7 @@ pub struct ColliderMassProperties { } impl ColliderMassProperties { + /// The collider has no mass. pub const ZERO: Self = Self { mass: Mass::ZERO, inverse_mass: InverseMass(Scalar::INFINITY), diff --git a/src/components/mod.rs b/src/components/mod.rs index 72dea84c..3494b2fa 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -179,6 +179,7 @@ pub struct PreviousPosition(pub Vector); pub struct LinearVelocity(pub Vector); impl LinearVelocity { + /// Zero linear velocity. pub const ZERO: LinearVelocity = LinearVelocity(Vector::ZERO); } @@ -214,8 +215,10 @@ pub struct AngularVelocity(pub Scalar); pub struct AngularVelocity(pub Vector); impl AngularVelocity { + /// Zero angular velocity. #[cfg(feature = "2d")] pub const ZERO: AngularVelocity = AngularVelocity(0.0); + /// Zero angular velocity. #[cfg(feature = "3d")] pub const ZERO: AngularVelocity = AngularVelocity(Vector::ZERO); } diff --git a/src/constraints/angular_constraint.rs b/src/constraints/angular_constraint.rs index b5aaba26..c7616870 100644 --- a/src/constraints/angular_constraint.rs +++ b/src/constraints/angular_constraint.rs @@ -77,6 +77,11 @@ pub trait AngularConstraint: XpbdConstraint<2> { p } + /// Computes the generalized inverse mass of a body when applying an angular correction + /// around `axis`. + /// + /// In 2D, `axis` should only have the z axis set to either -1 or 1 to indicate counterclockwise or + /// clockwise rotation. #[cfg(feature = "2d")] fn compute_generalized_inverse_mass(&self, body: &RigidBodyQueryItem, axis: Vector3) -> Scalar { if body.rb.is_dynamic() { @@ -87,6 +92,8 @@ pub trait AngularConstraint: XpbdConstraint<2> { } } + /// Computes the generalized inverse mass of a body when applying an angular correction + /// around `axis`. #[cfg(feature = "3d")] fn compute_generalized_inverse_mass(&self, body: &RigidBodyQueryItem, axis: Vector) -> Scalar { if body.rb.is_dynamic() { @@ -97,12 +104,14 @@ pub trait AngularConstraint: XpbdConstraint<2> { } } + /// Computes the update in rotation when applying an angular correction `p`. #[cfg(feature = "2d")] fn get_delta_rot(_rot: Rotation, inverse_inertia: Scalar, p: Scalar) -> Rotation { // Equation 8/9 but in 2D Rotation::from_radians(inverse_inertia * p) } + /// Computes the update in rotation when applying an angular correction `p`. #[cfg(feature = "3d")] fn get_delta_rot(rot: Rotation, inverse_inertia: Matrix3, p: Vector) -> Rotation { // Equation 8/9 diff --git a/src/constraints/joints/mod.rs b/src/constraints/joints/mod.rs index ca2bd0e6..f4477ad0 100644 --- a/src/constraints/joints/mod.rs +++ b/src/constraints/joints/mod.rs @@ -224,7 +224,9 @@ pub trait Joint: Component + PositionConstraint + AngularConstraint { /// A limit that indicates that the distance between two points should be between `min` and `max`. #[derive(Clone, Copy, Debug, PartialEq)] pub struct DistanceLimit { + /// The minimum distance between two points. pub min: Scalar, + /// The maximum distance between two points. pub max: Scalar, } @@ -281,7 +283,9 @@ impl DistanceLimit { /// A limit that indicates that angles should be between `alpha` and `beta`. #[derive(Clone, Copy, Debug, PartialEq)] pub struct AngleLimit { + /// The minimum angle. pub alpha: Scalar, + /// The maximum angle. pub beta: Scalar, } diff --git a/src/constraints/penetration.rs b/src/constraints/penetration.rs index 74eb518f..dc79552a 100644 --- a/src/constraints/penetration.rs +++ b/src/constraints/penetration.rs @@ -53,6 +53,7 @@ impl XpbdConstraint<2> for PenetrationConstraint { } impl PenetrationConstraint { + /// Creates a new [`PenetrationConstraint`] with the given bodies and contact data. pub fn new(body1: &RigidBodyQueryItem, body2: &RigidBodyQueryItem, contact: Contact) -> Self { let world_r1 = contact.point1 - body1.position.0 + body1.center_of_mass.0; let world_r2 = contact.point2 - body2.position.0 + body2.center_of_mass.0; diff --git a/src/constraints/position_constraint.rs b/src/constraints/position_constraint.rs index 9488503d..5976d402 100644 --- a/src/constraints/position_constraint.rs +++ b/src/constraints/position_constraint.rs @@ -42,6 +42,8 @@ pub trait PositionConstraint: XpbdConstraint<2> { p } + /// Computes the generalized inverse mass of a body when applying a positional correction + /// at point `r` along the vector `n`. #[cfg(feature = "2d")] fn compute_generalized_inverse_mass( &self, @@ -57,6 +59,8 @@ pub trait PositionConstraint: XpbdConstraint<2> { } } + /// Computes the generalized inverse mass of a body when applying a positional correction + /// at point `r` along the vector `n`. #[cfg(feature = "3d")] fn compute_generalized_inverse_mass( &self, @@ -78,12 +82,14 @@ pub trait PositionConstraint: XpbdConstraint<2> { } } + /// Computes the update in rotation when applying a positional correction `p` at point `r`. #[cfg(feature = "2d")] fn get_delta_rot(_rot: Rotation, inverse_inertia: Scalar, r: Vector, p: Vector) -> Rotation { // Equation 8/9 but in 2D Rotation::from_radians(inverse_inertia * r.perp_dot(p)) } + /// Computes the update in rotation when applying a positional correction `p` at point `r`. #[cfg(feature = "3d")] fn get_delta_rot(rot: Rotation, inverse_inertia: Matrix3, r: Vector, p: Vector) -> Rotation { // Equation 8/9 diff --git a/src/math/double.rs b/src/math/double.rs index 6bd93a44..5d827990 100644 --- a/src/math/double.rs +++ b/src/math/double.rs @@ -1,17 +1,25 @@ use super::AdjustPrecision; use bevy::math::*; +/// The floating point number type used by Bevy XPBD. pub type Scalar = f64; +/// The PI constant. pub const PI: Scalar = std::f64::consts::PI; +/// The vector type used by Bevy XPBD. #[cfg(feature = "2d")] pub type Vector = DVec2; +/// The vector type used by Bevy XPBD. #[cfg(feature = "3d")] pub type Vector = DVec3; +/// The vector type used by Bevy XPBD. This is always a 2D vector regardless of the chosen dimension. pub type Vector2 = DVec2; +/// The vector type used by Bevy XPBD. This is always a 3D vector regardless of the chosen dimension. pub type Vector3 = DVec3; +/// The 3x3 matrix type used by Bevy XPBD. pub type Matrix3 = DMat3; +/// The quaternion type used by Bevy XPBD. pub type Quaternion = DQuat; impl AdjustPrecision for f32 { diff --git a/src/math/mod.rs b/src/math/mod.rs index 323a3cd9..c3eb69aa 100644 --- a/src/math/mod.rs +++ b/src/math/mod.rs @@ -15,13 +15,17 @@ use bevy::math::*; /// Adjust the precision of the math construct to the precision chosen for compilation. pub trait AdjustPrecision { + /// A math construct type with the desired precision. type Adjusted; + /// Adjusts the precision of [`self`] to [`Self::Adjusted`](#associatedtype.Adjusted). fn adjust_precision(&self) -> Self::Adjusted; } /// Adjust the precision down to `f32` regardless of compilation. pub trait AsF32 { + /// The `f32` version of a math construct. type F32; + /// Returns the `f32` version of this type. fn as_f32(&self) -> Self::F32; } diff --git a/src/resources.rs b/src/resources.rs index e140ab53..ee490ac2 100644 --- a/src/resources.rs +++ b/src/resources.rs @@ -191,5 +191,6 @@ impl Default for Gravity { } impl Gravity { + /// Zero gravity. pub const ZERO: Gravity = Gravity(Vector::ZERO); } From 5e4165f7ff95c2594c19ae6e3f2dc45991601051 Mon Sep 17 00:00:00 2001 From: Jondolf Date: Mon, 19 Jun 2023 14:29:15 +0300 Subject: [PATCH 21/29] Improve constraint docs --- src/constraints/angular_constraint.rs | 4 +-- src/constraints/mod.rs | 40 +++++++++++++++++++++----- src/constraints/position_constraint.rs | 5 ++-- 3 files changed, 36 insertions(+), 13 deletions(-) diff --git a/src/constraints/angular_constraint.rs b/src/constraints/angular_constraint.rs index c7616870..c1054eac 100644 --- a/src/constraints/angular_constraint.rs +++ b/src/constraints/angular_constraint.rs @@ -1,8 +1,6 @@ use crate::prelude::*; -/// Angular constraints apply an angular correction of a given rotation angle around a given axis. -/// -/// The constraint functions are based on equations 11-16 in the paper [Detailed Rigid Body Simulation with Extended Position Based Dynamics](https://matthias-research.github.io/pages/publications/PBDBodies.pdf). +/// An angular constraint applies an angular correction around a given axis. pub trait AngularConstraint: XpbdConstraint<2> { /// Applies angular constraints for interactions between two bodies. /// diff --git a/src/constraints/mod.rs b/src/constraints/mod.rs index 54cade5b..0d4bd80f 100644 --- a/src/constraints/mod.rs +++ b/src/constraints/mod.rs @@ -57,7 +57,7 @@ //! } //! ``` //! -//! Take a look at [`XpbdConstraint`] and the constraint [theory](#theory) to learn more about what to put in `solve`. +//! Take a look at [`XpbdConstraint::solve`] and the constraint [theory](#theory) to learn more about what to put in `solve`. //! //! Next, we need to add a system that solves the constraint during each run of the [solver]. If your constraint is //! a component like most of Bevy XPBD's constraints, you can use the generic [`solve_constraint`] system that handles @@ -81,6 +81,9 @@ //! Now just spawn an instance of the constraint, give it the participating entities, and the constraint should be getting //! solved automatically according to the `solve` method! //! +//! You can find a working example of a custom constraint +//! [here](https://github.com/Jondolf/bevy_xpbd/blob/main/crates/bevy_xpbd_3d/examples/custom_constraint.rs). +//! //! ## Theory //! //! In this section, you can learn some of the theory behind how constraints work. Understanding the theory and maths isn't @@ -116,9 +119,9 @@ //! rest distance, the gradient vector would point away from the other particle, because it would increase the distance //! even further. //! -//! ### Lagrange multiplier +//! ### Lagrange multipliers //! -//! In the context of constraints, the Lagrange multiplier `λ` corresponds to the signed magnitude of the constraint force. +//! In the context of constraints, a Lagrange multiplier `λ` corresponds to the signed magnitude of the constraint force. //! It is a scalar value that is the same for all of the constraint's participating particles, and it is used for computing //! the correction that the constraint should apply to the particles along the gradients. //! @@ -203,18 +206,41 @@ pub use position_constraint::PositionConstraint; use crate::prelude::*; +/// A trait for all XPBD [constraints]. pub trait XpbdConstraint { /// The entities participating in the constraint. fn entities(&self) -> [Entity; ENTITY_COUNT]; /// Solves the constraint. + /// + /// There are two main steps to solving a constraint: + /// + /// 1. Compute the generalized inverse masses, [gradients](constraints#constraint-gradients) + /// and the [Lagrange multiplier](constraints#lagrange-multipliers) update. + /// 2. Apply corrections along the gradients using the Lagrange multiplier update. + /// + /// [`XpbdConstraint`] provides the [`compute_lagrange_update`](XpbdConstraint::compute_lagrange_update) + /// method for all constraints. It requires the gradients and inverse masses of the participating entities. + /// + /// For constraints between two bodies, you can implement [`PositionConstraint`]. and [`AngularConstraint`] + /// to get the associated `compute_generalized_inverse_mass`, `apply_positional_correction` and + /// `apply_angular_correction` methods. Otherwise you must implement the generalized inverse mass + /// computations and correction applying logic yourself. + /// + /// You can find a working example of a custom constraint + /// [here](https://github.com/Jondolf/bevy_xpbd/blob/main/crates/bevy_xpbd_3d/examples/custom_constraint.rs). fn solve(&mut self, bodies: [&mut RigidBodyQueryItem; ENTITY_COUNT], dt: Scalar); - /// Computes how much a constraint's Lagrange multiplier changes when projecting the constraint for all participating particles. + /// Computes how much a constraint's [Lagrange multiplier](constraints#lagrange-multipliers) changes when projecting + /// the constraint for all participating particles. + /// + /// `c` is a scalar value returned by the [constraint function](constraints#constraint-functions). + /// When it is zero, the constraint is satisfied. /// - /// `c` is a scalar value returned by the constraint function. When it is zero, the constraint is satisfied. + /// Each particle should have a corresponding [gradient](constraints#constraint-gradients) in `gradients`. + /// A gradient is a vector that refers to the direction in which `c` increases the most. /// - /// Each particle should have a corresponding gradient in `gradients`. A gradient is a vector that refers to the direction in which `c` increases the most. + /// See the [constraint theory](#theory) for more information. fn compute_lagrange_update( &self, lagrange: Scalar, @@ -241,6 +267,6 @@ pub trait XpbdConstraint { (-c - tilde_compliance * lagrange) / (w_sum + tilde_compliance) } - /// Sets the constraint's Lagrange multipliers to 0. + /// Sets the constraint's [Lagrange multipliers](constraints#lagrange-multipliers) to 0. fn clear_lagrange_multipliers(&mut self); } diff --git a/src/constraints/position_constraint.rs b/src/constraints/position_constraint.rs index 5976d402..9476ba30 100644 --- a/src/constraints/position_constraint.rs +++ b/src/constraints/position_constraint.rs @@ -1,8 +1,7 @@ use crate::prelude::*; -/// Positional constraints apply a position correction with a given direction and magnitude at the local contact points `r1` and `r2`. -/// -/// The constraint functions are based on equations 2-9 in the paper [Detailed Rigid Body Simulation with Extended Position Based Dynamics](https://matthias-research.github.io/pages/publications/PBDBodies.pdf). +/// A positional constraint applies a positional correction +/// with a given direction and magnitude at the local contact points `r1` and `r2`. pub trait PositionConstraint: XpbdConstraint<2> { /// Applies a positional correction to two bodies. /// From c7b215796214a59fa394128e01a0143986d384ae Mon Sep 17 00:00:00 2001 From: Jondolf Date: Mon, 19 Jun 2023 14:29:49 +0300 Subject: [PATCH 22/29] Improve docs of individual plugins --- src/plugins/broad_phase.rs | 13 ++++++++++--- src/plugins/debug.rs | 3 +++ src/plugins/integrator.rs | 13 +++++++++++-- src/plugins/mod.rs | 6 +++--- src/plugins/prepare.rs | 15 +++++++++++++-- src/plugins/setup.rs | 4 +++- src/plugins/sleeping.rs | 16 ++++++++++++---- src/plugins/solver.rs | 27 ++++++++++++++++++--------- src/plugins/sync.rs | 11 +++++++++-- 9 files changed, 82 insertions(+), 26 deletions(-) diff --git a/src/plugins/broad_phase.rs b/src/plugins/broad_phase.rs index bd229106..9183862e 100644 --- a/src/plugins/broad_phase.rs +++ b/src/plugins/broad_phase.rs @@ -1,11 +1,18 @@ -//! The broad phase is responsible for collecting potential collision pairs into the [`BroadCollisionPairs`] resource using simple AABB intersection checks. This reduces the number of required precise collision checks. See [`BroadPhasePlugin`]. +//! Collects pairs of potentially colliding entities into [`BroadCollisionPairs`] using +//! [AABB](ColliderAabb) intersection checks. +//! +//! See [`BroadPhasePlugin`]. use crate::prelude::*; use bevy::{prelude::*, utils::StableHashSet}; -/// The `BroadPhasePlugin` is responsible for collecting potential collision pairs into the [`BroadCollisionPairs`] resource using simple AABB intersection checks. +/// Collects pairs of potentially colliding entities into [`BroadCollisionPairs`] using +/// [AABB](ColliderAabb) intersection checks. This speeds up narrow phase collision detection, +/// as the number of precise collision checks required is greatly reduced. /// -/// The broad phase speeds up collision detection, as the number of accurate collision checks is greatly reduced. +/// Currently, the broad phase uses the [sweep and prune](https://en.wikipedia.org/wiki/Sweep_and_prune) algorithm. +/// +/// The broad phase systems run in [`PhysicsSet::BroadPhase`]. pub struct BroadPhasePlugin; impl Plugin for BroadPhasePlugin { diff --git a/src/plugins/debug.rs b/src/plugins/debug.rs index dc8ce24b..c3bc8c1c 100644 --- a/src/plugins/debug.rs +++ b/src/plugins/debug.rs @@ -1,4 +1,5 @@ //! Renders physics objects and events like [AABBs](ColliderAabb) and [contacts](Collision) for debugging purposes. +//! //! See [`PhysicsDebugPlugin`]. use crate::prelude::*; @@ -6,6 +7,8 @@ use bevy::prelude::*; use bevy_prototype_debug_lines::*; /// Renders physics objects and events like [AABBs](ColliderAabb) and [contacts](Collision) for debugging purposes. +/// +/// You can configure what is rendered using the [`PhysicsDebugConfig`] resource. pub struct PhysicsDebugPlugin; impl Plugin for PhysicsDebugPlugin { diff --git a/src/plugins/integrator.rs b/src/plugins/integrator.rs index d65f7d9e..9abc7783 100644 --- a/src/plugins/integrator.rs +++ b/src/plugins/integrator.rs @@ -1,9 +1,18 @@ -//! The integrator explicitly integrates the positions and velocities of bodies. See [`IntegratorPlugin`]. +//! Integrates Newton's 2nd law of motion, applying forces and moving entities according to their velocities. +//! +//! See [`IntegratorPlugin`]. use crate::prelude::*; use bevy::prelude::*; -/// The `IntegratorPlugin` explicitly integrates the positions and velocities of bodies taking only external forces like gravity into account. This acts as a prediction for the next positions and orientations of the bodies. +/// Integrates Newton's 2nd law of motion, applying forces and moving entities according to their velocities. +/// +/// This acts as a prediction for the next positions and orientations of the bodies. The [solver] corrects these predicted +/// positions to follow the rules set by the [constraints]. +/// +/// The integration scheme used is very closely related to implicit Euler integration. +/// +/// The integration systems run in [`SubstepSet::Integrate`]. pub struct IntegratorPlugin; impl Plugin for IntegratorPlugin { diff --git a/src/plugins/mod.rs b/src/plugins/mod.rs index 35189831..808a5de9 100644 --- a/src/plugins/mod.rs +++ b/src/plugins/mod.rs @@ -56,7 +56,7 @@ use bevy::prelude::*; /// - [`SolverPlugin`]: Solves positional and angular [constraints], updates velocities and solves velocity constraints /// (dynamic [friction](Friction) and [restitution](Restitution)). /// - [`SleepingPlugin`]: Controls when bodies should be deactivated and marked as [`Sleeping`] to improve performance. -/// - [`SyncPlugin`]: Synchronizes the engine's [`Position`] and [`Rotation`] with Bevy's [`Transform`]s. +/// - [`SyncPlugin`]: Synchronizes the engine's [`Position`]s and [`Rotation`]s with Bevy's [`Transform`]s. /// - `PhysicsDebugPlugin`: Renders physics objects and events like [AABBs](ColliderAabb) and [contacts](Collision) /// for debugging purposes (only with `debug-plugin` feature enabled). /// @@ -203,7 +203,7 @@ pub enum SubstepSet { /// the constraint system to this set. See [`solve_constraint`]. SolveUserConstraints, /// In the velocity update step, new velocities are derived for all particles and bodies after the position solving step. - UpdateVel, + UpdateVelocities, /// During the velocity solving step, a velocity update caused by properties like restitution and friction will be applied to all particles and bodies. - SolveVel, + SolveVelocities, } diff --git a/src/plugins/prepare.rs b/src/plugins/prepare.rs index c698fc62..e85755e2 100644 --- a/src/plugins/prepare.rs +++ b/src/plugins/prepare.rs @@ -1,9 +1,20 @@ -//! Performs necessary preparations and updates at the start of each physics frame. See [`PreparePlugin`]. +//! Runs systems at the start of each physics frame; initializes [rigid bodies](RigidBody) +//! and [colliders](Collider) and updates components. +//! +//! See [`PreparePlugin`]. use crate::{prelude::*, utils::make_isometry}; use bevy::prelude::*; -/// Performs necessary preparations and updates at the start of each physics frame. For example, [`ColliderAabb`]s and mass properties are updated. +/// Runs systems at the start of each physics frame; initializes [rigid bodies](RigidBody) +/// and [colliders](Collider) and updates components. +/// +/// - Adds missing rigid body components for entities with a [`RigidBody`] component +/// - Adds missing collider components for entities with a [`Collider`] component +/// - Updates [AABBs](ColliderAabb) +/// - Updates mass properties and adds [`ColliderMassProperties`] on top of the existing mass properties +/// +/// The systems run in [`PhysicsSet::Prepare`]. pub struct PreparePlugin; impl Plugin for PreparePlugin { diff --git a/src/plugins/setup.rs b/src/plugins/setup.rs index ff379034..f3158be3 100644 --- a/src/plugins/setup.rs +++ b/src/plugins/setup.rs @@ -1,4 +1,6 @@ -//! Sets up the physics engine by initializing the necessary schedules, sets and resources. See [`PhysicsSetupPlugin`]. +//! Sets up the physics engine by initializing the necessary schedules, sets and resources. +//! +//! See [`PhysicsSetupPlugin`]. use crate::prelude::*; diff --git a/src/plugins/sleeping.rs b/src/plugins/sleeping.rs index 60fc0427..a85f0834 100644 --- a/src/plugins/sleeping.rs +++ b/src/plugins/sleeping.rs @@ -1,13 +1,21 @@ -//! Controls when bodies are active. This improves performance and helps prevent jitter. See [`Sleeping`]. +//! Controls when bodies should be deactivated and marked as [`Sleeping`] to improve performance. +//! +//! See [`SleepingPlugin`]. use crate::prelude::*; use bevy::prelude::*; -/// Controls when bodies are active. This improves performance and helps prevent jitter. +/// Controls when bodies should be deactivated and marked as [`Sleeping`] to improve performance. /// -/// Bodies are marked as [`Sleeping`] when their linear and angular velocities are below the [`SleepingThreshold`] for a duration indicated by [`DeactivationTime`]. +/// Bodies are marked as [`Sleeping`] when their linear and angular velocities are below the [`SleepingThreshold`] +/// for a duration indicated by [`DeactivationTime`]. /// -/// Bodies are woken up when an active body or constraint interacts with them, or when gravity changes, or when the body's position, rotation, velocity, or external forces are changed. +/// Bodies are woken up when an active body or constraint interacts with them, or when gravity changes, +/// or when the body's position, rotation, velocity, or external forces are changed. +/// +/// This plugin does *not* handle constraints waking up bodies. That is done by the [solver]. +/// +/// The sleeping systems run in [`PhysicsSet::Sleeping`]. pub struct SleepingPlugin; impl Plugin for SleepingPlugin { diff --git a/src/plugins/solver.rs b/src/plugins/solver.rs index 2e6c950b..1208cf4d 100644 --- a/src/plugins/solver.rs +++ b/src/plugins/solver.rs @@ -1,4 +1,7 @@ -//! The XPBD solver is responsible for constraint projection, velocity updates, and velocity corrections. See [`SolverPlugin`]. +//! Solves positional and angular [constraints], updates velocities and solves velocity constraints +//! (dynamic [friction](Friction), [restitution](Restitution) and [joint damping](joints#damping)). +//! +//! See [`SolverPlugin`]. use crate::{ collision::*, @@ -8,19 +11,25 @@ use crate::{ use bevy::prelude::*; use constraints::penetration::PenetrationConstraint; -/// The `SolverPlugin` is reponsible for constraint projection, velocity updates, and velocity corrections caused by dynamic friction, restitution and damping. +/// Solves positional and angular [constraints], updates velocities and solves velocity constraints +/// (dynamic [friction](Friction) and [restitution](Restitution) and [joint damping](joints#damping)). /// /// ## Steps /// /// Below are the three main steps of the `SolverPlugin`. /// -/// 1. **Constraint projection**: Constraints are handled by looping through them and applying positional and angular corrections to the bodies in order to satisfy the constraints. +/// 1. **Constraint projection**: Constraints are handled by looping through them and applying positional and angular corrections +/// to the bodies in order to satisfy the constraints. Runs in [`SubstepSet::SolveConstraints`] and [`SubstepSet::SolveUserConstraints`]. /// /// 2. **Velocity update**: The velocities of bodies are updated based on positional and rotational changes from the last step. +/// Runs in [`SubstepSet::UpdateVelocities`]. /// -/// 3. **Velocity solve**: Velocity corrections caused by dynamic friction, restitution and damping are applied. +/// 3. **Velocity solve**: Velocity corrections caused by dynamic friction, restitution and joint damping are applied. +/// Runs in [`SubstepSet::SolveVelocities`]. /// -/// In the case of collisions, [`PenetrationConstraint`]s are created for each contact pair. The constraints are resolved by moving the bodies so that they no longer penetrate. Then, the velocities are updated, and velocity corrections caused by dynamic friction and restitution are applied. +/// In the case of collisions, [`PenetrationConstraint`]s are created for each contact pair. +/// The constraints are resolved by moving the bodies so that they no longer penetrate. +/// Then, the velocities are updated, and velocity corrections caused by dynamic friction and restitution are applied. pub struct SolverPlugin; impl Plugin for SolverPlugin { @@ -39,8 +48,8 @@ impl Plugin for SolverPlugin { SubstepSet::Integrate, SubstepSet::SolveConstraints, SubstepSet::SolveUserConstraints, - SubstepSet::UpdateVel, - SubstepSet::SolveVel, + SubstepSet::UpdateVelocities, + SubstepSet::SolveVelocities, ) .chain(), ); @@ -57,7 +66,7 @@ impl Plugin for SolverPlugin { .in_set(SubstepSet::SolveConstraints), ); - substeps.add_systems((update_lin_vel, update_ang_vel).in_set(SubstepSet::UpdateVel)); + substeps.add_systems((update_lin_vel, update_ang_vel).in_set(SubstepSet::UpdateVelocities)); substeps.add_systems( ( @@ -68,7 +77,7 @@ impl Plugin for SolverPlugin { joint_damping::, ) .chain() - .in_set(SubstepSet::SolveVel), + .in_set(SubstepSet::SolveVelocities), ); } } diff --git a/src/plugins/sync.rs b/src/plugins/sync.rs index 2b5da562..245620f0 100644 --- a/src/plugins/sync.rs +++ b/src/plugins/sync.rs @@ -1,9 +1,16 @@ -//! Synchronizes changes from the physics world to Bevy [`Transform`]s. +//! Synchronizes the engine's [`Position`]s and [`Rotation`]s with Bevy's [`Transform`]s. +//! +//! See [`SyncPlugin`]. use crate::{prelude::*, PhysicsSchedule}; use bevy::prelude::*; -/// Synchronizes changes from the physics world to Bevy [`Transform`]s. +/// Synchronizes the engine's [`Position`]s and [`Rotation`]s with Bevy's [`Transform`]s. +/// +/// Currently, the transforms of nested bodies are updated to reflect their global positions. +/// This means that nested [rigid bodies](RigidBody) can behave independently regardless of the hierarchy. +/// +/// The synchronization systems run in [`PhysicsSet::Sync`]. pub struct SyncPlugin; impl Plugin for SyncPlugin { From 5e9975b58b8a7a584e2bd39283edcb80a05c39be Mon Sep 17 00:00:00 2001 From: Jondolf Date: Mon, 19 Jun 2023 15:04:45 +0300 Subject: [PATCH 23/29] Fix `Rotation` doc examples --- src/components/rotation.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/rotation.rs b/src/components/rotation.rs index 9ed26b2d..b4ec7b17 100644 --- a/src/components/rotation.rs +++ b/src/components/rotation.rs @@ -22,7 +22,7 @@ use crate::prelude::*; /// /// fn setup(mut commands: Commands) { /// // Spawn a dynamic rigid body rotated by 90 degrees -/// commands.spawn((RigidBody::Dynamic, Rotation::from_degrees(90))) +/// commands.spawn((RigidBody::Dynamic, Rotation::from_degrees(90.0))); /// } /// ``` #[cfg(feature = "2d")] @@ -41,12 +41,15 @@ pub struct Rotation { /// /// ``` /// use bevy::prelude::*; -/// use bevy_xpbd_2d::prelude::*; +/// use bevy_xpbd_3d::prelude::*; /// +/// # #[cfg(feature = "f32")] /// fn setup(mut commands: Commands) { /// // Spawn a dynamic rigid body rotated by 1.5 radians around the x axis -/// commands.spawn((RigidBody::Dynamic, Rotation::from_rotation_x(1.5))) +/// commands.spawn((RigidBody::Dynamic, Rotation(Quat::from_rotation_x(1.5)))); /// } +/// # #[cfg(not(feature = "f32"))] +/// # fn setup() {} /// ``` #[cfg(feature = "3d")] #[derive(Reflect, Clone, Copy, Component, Debug, Default, Deref, DerefMut)] From b0723a0c182b472bca38a05c7fbad72fbee9d0fc Mon Sep 17 00:00:00 2001 From: Jondolf Date: Mon, 19 Jun 2023 15:05:32 +0300 Subject: [PATCH 24/29] Improve docs of `Sensor`, `CollidingEntities` and collision events --- src/collision.rs | 6 ++--- src/components/collider.rs | 46 ++++++++++++++++++++++++++++++++++---- 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/src/collision.rs b/src/collision.rs index 53de98cb..8256a553 100644 --- a/src/collision.rs +++ b/src/collision.rs @@ -2,15 +2,15 @@ use crate::prelude::*; -/// An event that is sent for each contact pair during the narrow phase. +/// A [collision event](Collider#collision-events) that is sent for each contact pair during the narrow phase. #[derive(Clone, Debug, PartialEq)] pub struct Collision(pub Contact); -/// An event that is sent when two entities start colliding. +/// A [collision event](Collider#collision-events) that is sent when two entities start colliding. #[derive(Clone, Debug, PartialEq)] pub struct CollisionStarted(pub Entity, pub Entity); -/// An event that is sent when two entities stop colliding. +/// A [collision event](Collider#collision-events) that is sent when two entities stop colliding. #[derive(Clone, Debug, PartialEq)] pub struct CollisionEnded(pub Entity, pub Entity); diff --git a/src/components/collider.rs b/src/components/collider.rs index 712f818c..5e4508e8 100644 --- a/src/components/collider.rs +++ b/src/components/collider.rs @@ -306,8 +306,25 @@ fn extract_mesh_vertices_indices(mesh: &Mesh) -> Option { Some((vtx, idx)) } -/// Marks a [`Collider`] as a sensor collider. Sensor colliders send collision events but -/// don't cause a collision response. This is often used to detect when something enters or leaves an area. +/// A component that marks a [`Collider`] as a sensor collider. +/// +/// Sensor colliders send [collision events](Collider#collision-events) but don't cause a collision response. +/// This is often used to detect when something enters or leaves an area. +/// +/// ## Example +/// +/// ``` +/// use bevy::prelude::*; +/// # #[cfg(feature = "2d")] +/// # use bevy_xpbd_2d::prelude::*; +/// # #[cfg(feature = "3d")] +/// use bevy_xpbd_3d::prelude::*; +/// +/// fn setup(mut commands: Commands) { +/// // Spawn a static ball that generates collision events but doesn't cause a collision response +/// commands.spawn((RigidBody::Static, Collider::ball(0.5), Sensor)); +/// } +/// ``` #[derive(Reflect, Clone, Component, Debug, Default, PartialEq, Eq)] pub struct Sensor; @@ -328,8 +345,29 @@ impl Default for ColliderAabb { } } -/// Contains the entities that are colliding with an entity. These entities are added by the [`SolverPlugin`] -/// when collisions are detected during the constraint solve. +/// Contains the entities that are colliding with an entity. +/// +/// This component is automatically added for all entities with a [`Collider`]. +/// +/// ## Example +/// +/// ``` +/// use bevy::prelude::*; +/// # #[cfg(feature = "2d")] +/// # use bevy_xpbd_2d::prelude::*; +/// # #[cfg(feature = "3d")] +/// use bevy_xpbd_3d::prelude::*; +/// +/// fn my_system(query: Query<(Entity, &CollidingEntities)>) { +/// for (entity, colliding_entities) in &query { +/// println!( +/// "{:?} is colliding with the following entities: {:?}", +/// entity, +/// colliding_entities +/// ); +/// } +/// } +/// ``` #[derive(Reflect, Clone, Component, Debug, Default, Deref, DerefMut, PartialEq, Eq)] #[reflect(Component)] pub struct CollidingEntities(pub HashSet); From 1ebc51b78da754b29eca76a5468a23e6e2dafd45 Mon Sep 17 00:00:00 2001 From: Jondolf Date: Mon, 19 Jun 2023 16:15:38 +0300 Subject: [PATCH 25/29] Improve system set docs and fix typos --- src/components/mod.rs | 11 +++++---- src/plugins/mod.rs | 54 +++++++++++++++++++++++++++++++------------ 2 files changed, 46 insertions(+), 19 deletions(-) diff --git a/src/components/mod.rs b/src/components/mod.rs index 3494b2fa..3469fbfb 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -57,7 +57,7 @@ use derive_more::From; /// You can change any of these during initialization and runtime in order to alter the behaviour of the body. /// /// Note that by default, rigid bodies don't have any mass, so dynamic bodies will gain infinite velocity upon any interaction. -/// See the [section below](#adding-mass-properties] for how to add mass properties. +/// See the [section below](#adding-mass-properties) for how to add mass properties. /// /// ## Adding mass properties /// @@ -86,11 +86,13 @@ use derive_more::From; pub enum RigidBody { /// Dynamic bodies are bodies that are affected by forces, velocity and collisions. /// - /// You should generally move dynamic bodies by modifying the [`ExternalForce`], [`LinearVelocity`] or [`AngularVelocity`] components. Directly changing the [`Position`] or [`Rotation`] works as well, but it may cause unwanted behaviour if the body happens to teleport into the colliders of other bodies. + /// You should generally move dynamic bodies by modifying the [`ExternalForce`], [`ExternalTorque`], [`LinearVelocity`] and [`AngularVelocity`] components. + /// Directly changing the [`Position`] or [`Rotation`] works as well, but it may cause unwanted behaviour if the body happens to teleport into the colliders of other bodies. #[default] Dynamic, - /// Static bodies are not affected by any forces, collisions or velocity, and they act as if they have an infinite mass and moment of inertia. The only way to move a static body is to manually change its position. + /// Static bodies are not affected by any forces, collisions or velocity, and they act as if they have an infinite mass and moment of inertia. + /// The only way to move a static body is to manually change its position. /// /// Collisions with static bodies will affect dynamic bodies, but not other static bodies or kinematic bodies. /// @@ -99,7 +101,8 @@ pub enum RigidBody { /// Kinematic bodies are bodies that are not affected by any external forces or collisions. They will realistically affect colliding dynamic bodies, but not other kinematic bodies. /// - /// Unlike static bodies, the [`Position`], [`LinearVelocity`] and [`AngularVelocity`] components will move kinematic bodies as expected. These components will never be altered by the physics engine, so you can kinematic bodies freely. + /// Unlike static bodies, the [`Position`], [`LinearVelocity`] and [`AngularVelocity`] components will move kinematic bodies as expected. + /// These components will never be altered by the physics engine, so you can move kinematic bodies freely. Kinematic, } diff --git a/src/plugins/mod.rs b/src/plugins/mod.rs index 808a5de9..5a7cae4a 100644 --- a/src/plugins/mod.rs +++ b/src/plugins/mod.rs @@ -167,17 +167,27 @@ impl PluginGroup for PhysicsPlugins { /// 5. Sync data #[derive(SystemSet, Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum PhysicsSet { - /// In the preparation step, necessary preparations and updates will be run before the rest of the physics simulation loop. + /// Responsible for initializing [rigid bodies](RigidBody) and [colliders](Collider) and + /// updating several components. + /// + /// See [`PreparePlugin`]. Prepare, - /// During the broad phase, potential collisions will be collected into the [`BroadCollisionPairs`] resource using simple AABB intersection checks. + /// Responsible for collecting pairs of potentially colliding entities into [`BroadCollisionPairs`] using + /// [AABB](ColliderAabb) intersection tests. /// - /// The broad phase speeds up collision detection, as the number of accurate collision checks is greatly reduced. + /// See [`BroadPhasePlugin`]. BroadPhase, - /// Substepping is an inner loop inside a physics step. See [`SubstepSet`] and [`SubstepSchedule`]. + /// Responsible for substepping, which is an inner loop inside a physics step. + /// + /// See [`SubstepSet`] and [`SubstepSchedule`]. Substeps, - /// The sleeping step controls when bodies are active. This improves performance and helps prevent jitter. See [`Sleeping`]. + /// Responsible for controlling when bodies should be deactivated and marked as [`Sleeping`]. + /// + /// See [`SleepingPlugin`]. Sleeping, - /// In the sync step, Bevy [`Transform`]s are synchronized with the physics world. + /// Responsible for synchronizing [`Position`]s and [`Rotation`]s with Bevy's [`Transform`]s. + /// + /// See [`SyncPlugin`]. Sync, } @@ -189,21 +199,35 @@ pub enum PhysicsSet { /// 4. Solve velocity constraints (dynamic friction and restitution) #[derive(SystemSet, Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum SubstepSet { - /// In the integration step, the position and velocity of each particle and body is explicitly integrated, taking only external forces like gravity (and forces applied by the user) into account. + /// Responsible for integrating Newton's 2nd law of motion, + /// applying forces and moving entities according to their velocities. + /// + /// See [`IntegratorPlugin`]. Integrate, - /// The solver iterates through constraints and solves them. - /// This step is also responsible for narrow phase collision detection, as it creates a [`PenetrationConstraint`] for each contact. + /// The [solver] iterates through [constraints] and solves them. + /// This step is also responsible for narrow phase collision detection, + /// as it creates a [`PenetrationConstraint`] for each contact. /// - /// **Note**: If you want to create your own constraints, you should add them in [`SubstepSet::SolveUserConstraints`] + /// **Note**: If you want to [create your own constraints](constraints#custom-constraints), + /// you should add them in [`SubstepSet::SolveUserConstraints`] /// to avoid system order ambiguities. + /// + /// See [`SolverPlugin`]. SolveConstraints, - /// The position solver iterates through custom constraints created by the user and solves them. + /// The [solver] iterates through custom [constraints] created by the user and solves them. + /// + /// You can [create new constraints](constraints#custom-constraints) by implementing [`XpbdConstraint`] + /// for a component and adding the [constraint system](solve_constraint) to this set. /// - /// You can create new constraints by implementing [`XpbdConstraint`] for a component and adding - /// the constraint system to this set. See [`solve_constraint`]. + /// See [`SolverPlugin`]. SolveUserConstraints, - /// In the velocity update step, new velocities are derived for all particles and bodies after the position solving step. + /// Responsible for updating velocities after [constraint](constraints) solving. + /// + /// See [`SolverPlugin`]. UpdateVelocities, - /// During the velocity solving step, a velocity update caused by properties like restitution and friction will be applied to all particles and bodies. + /// Responsible for applying dynamic friction, restitution and joint damping at the end of thei + /// substepping loop. + /// + /// See [`SolverPlugin`]. SolveVelocities, } From 2e5e01b9443d17a1b0c80e4cdd9340f227b3b81b Mon Sep 17 00:00:00 2001 From: Jondolf Date: Mon, 19 Jun 2023 16:45:07 +0300 Subject: [PATCH 26/29] Remove unnecessary impls --- src/components/mod.rs | 44 +------------------------------------------ 1 file changed, 1 insertion(+), 43 deletions(-) diff --git a/src/components/mod.rs b/src/components/mod.rs index 3469fbfb..840e44d9 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -157,20 +157,6 @@ pub struct SleepingDisabled; #[reflect(Component)] pub struct Position(pub Vector); -#[cfg(all(feature = "2d", feature = "f64"))] -impl From for Position { - fn from(value: Vec2) -> Self { - value.as_dvec2().into() - } -} - -#[cfg(all(feature = "3d", feature = "f64"))] -impl From for Position { - fn from(value: Vec3) -> Self { - value.as_dvec3().into() - } -} - /// The previous position of a body. #[derive(Reflect, Clone, Copy, Component, Debug, Default, Deref, DerefMut, PartialEq, From)] #[reflect(Component)] @@ -186,26 +172,12 @@ impl LinearVelocity { pub const ZERO: LinearVelocity = LinearVelocity(Vector::ZERO); } -#[cfg(all(feature = "2d", feature = "f64"))] -impl From for LinearVelocity { - fn from(value: Vec2) -> Self { - value.as_dvec2().into() - } -} - -#[cfg(all(feature = "3d", feature = "f64"))] -impl From for LinearVelocity { - fn from(value: Vec3) -> Self { - value.as_dvec3().into() - } -} - /// The linear velocity of a body before the velocity solve is performed. #[derive(Reflect, Clone, Copy, Component, Debug, Default, Deref, DerefMut, PartialEq, From)] #[reflect(Component)] pub struct PreSolveLinearVelocity(pub Vector); -/// The angular velocity of a body in radians. Positive values will result in counter-clockwise rotation. +/// The angular velocity of a body in radians. Positive values will result in counterclockwise rotation. #[cfg(feature = "2d")] #[derive(Reflect, Clone, Copy, Component, Debug, Default, PartialEq, From)] #[reflect(Component)] @@ -226,13 +198,6 @@ impl AngularVelocity { pub const ZERO: AngularVelocity = AngularVelocity(Vector::ZERO); } -#[cfg(all(feature = "3d", feature = "f64"))] -impl From for AngularVelocity { - fn from(value: Vec3) -> Self { - value.as_dvec3().into() - } -} - /// The angular velocity of a body in radians before the velocity solve is performed. Positive values will result in counter-clockwise rotation. #[cfg(feature = "2d")] #[derive(Reflect, Clone, Copy, Component, Debug, Default, PartialEq, From)] @@ -269,13 +234,6 @@ impl FloatZero for Scalar { #[reflect(Component)] pub struct ExternalTorque(pub Torque); -#[cfg(all(feature = "3d", feature = "f64"))] -impl From for ExternalTorque { - fn from(value: Vec3) -> Self { - value.as_dvec3().into() - } -} - /// Determines how coefficients are combined. The default is `Average`. /// /// When combine rules clash with each other, the following priority order is used: `Max > Multiply > Min > Average`. From 2ed7f6f1096d8e65d2ab7afd843947f0657e1cca Mon Sep 17 00:00:00 2001 From: Jondolf Date: Mon, 19 Jun 2023 16:45:28 +0300 Subject: [PATCH 27/29] Improve crate level docs --- src/lib.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 0e9bbecc..031eabe5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,11 +7,13 @@ //! //! Below are some of the core design principles used in Bevy XPBD. //! -//! - Made with Bevy, for Bevy. -//! - Use the ECS as much as possible. A Bevy physics engine shouldn't need to maintain a separate physics world. -//! - Provide an ergonomic and familiar API. Creating and using [rigid bodies](RigidBody) and [colliders](Collider) shouldn't be hard. -//! - Use a highly modular [plugin architecture](plugins). Many large projects require custom-made solutions, so users should be able to +//! - Made with Bevy, for Bevy. No wrappers around existing engines. +//! - Provide an ergonomic and familiar API. Ergonomics is key for a good experience. +//! - Utilize the ECS as much as possible. The engine should feel like a part of Bevy, and it shouldn't +//! need to maintain a separate physics world. +//! - Use highly modular [plugin architecture](plugins). Users should be able to //! replace parts of the engine with their own implementations. +//! - Have good documentation. A physics engine is pointless if you don't know how to use it. //! //! ## Features //! @@ -139,7 +141,7 @@ //! the dimension and precision: //! //! ```bash -//! cargo run --example cubes --no-default-features --features 3d,f32 +//! cargo run --example cubes --no-default-features --features "3d f32" //! ``` //! //! ### Common tasks @@ -160,7 +162,7 @@ //! //! ## Troubleshooting //! -//! ### Nothing is happening! +//! ### Why is nothing happening? //! //! Make sure you have added the [`PhysicsPlugins`] plugin group and you have given your rigid bodies //! a [`RigidBody`] component. See the [getting started](#getting-started) section. From caf752b0c93457b9cb2fa425275844c9667f4126 Mon Sep 17 00:00:00 2001 From: Jondolf Date: Mon, 19 Jun 2023 16:56:14 +0300 Subject: [PATCH 28/29] Add `MassPropertiesBundle` docs --- src/components/mass_properties.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/components/mass_properties.rs b/src/components/mass_properties.rs index 8ff79be7..8154b7d8 100644 --- a/src/components/mass_properties.rs +++ b/src/components/mass_properties.rs @@ -155,6 +155,27 @@ impl CenterOfMass { pub const ZERO: Self = Self(Vector::ZERO); } +/// A bundle containing mass properties. +/// +/// ## Example +/// +/// The easiest way to create a new bundle is to use the [new_computed](#method.new_computed) method +/// that computes the mass properties based on a given [`Collider`] and density. +/// +/// ``` +/// use bevy::prelude::*; +/// # #[cfg(feature = "2d")] +/// # use bevy_xpbd_2d::prelude::*; +/// # #[cfg(feature = "3d")] +/// use bevy_xpbd_3d::prelude::*; +/// +/// fn setup(mut commands: Commands) { +/// commands.spawn(( +/// RigidBody::Dynamic, +/// MassPropertiesBundle::new_computed(&Collider::ball(0.5), 1.0) +/// )); +/// } +/// ``` #[allow(missing_docs)] #[derive(Bundle, Debug, Default, Clone, PartialEq)] pub struct MassPropertiesBundle { From 4e0c03d656ab5a4da0ca8ebe08e71dd9c7b8e3ad Mon Sep 17 00:00:00 2001 From: Jondolf Date: Mon, 19 Jun 2023 17:04:43 +0300 Subject: [PATCH 29/29] Clarify a doc comment --- src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 031eabe5..1344dd84 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -54,9 +54,9 @@ //! bevy_xpbd_3d = "0.1" //! ``` //! -//! By default, Bevy XPBD uses `f32` numbers. If you encounter instability or use small [timesteps](PhysicsTimestep), -//! you might want to use `f64` instead. You can change these kinds of features by disabling -//! the default features and manually specifying the feature flags you want: +//! By default, Bevy XPBD uses `f32` numbers. If you encounter instability or use a large number +//! of [substeps](SubstepCount), you might want to use `f64` instead. You can change these kinds +//! of features by disabling the default features and manually specifying the feature flags you want: //! //! ```toml //! [dependencies]