Giter Club home page Giter Club logo

bevy_xpbd's Introduction

Hello there ๐Ÿ‘‹

I am a high school student interested in various different topics, such as science, technology, web development, art, games, and game development. In my spare time I like to develop projects that allow me to dive deeper into these areas, and I'm always looking to try out new things.

Currently I am maintaining and developing Bevy XPBD, a Bevy physics engine that uses Extended Position Based Dynamics. The Bevy ecosystem is in need of a native ECS-based physics engine, and my goal is to help reach that milestone. This project is my primary focus in terms of software development.

I have also built Algorust, an algorithm visualization website made almost entirely with Rust and the Yew frontend framework. Currently it just contains interactive visualizations for various sorting and pathfinding algorithms, but I might return to the project to add more at some point.

I also have a personal website, https://joonaa.dev! There you can find my blog and projects.

bevy_xpbd's People

Contributors

aceeri avatar aztro-dev avatar bnyu avatar dasisdormax avatar datael avatar felixbjorkeson avatar friz64 avatar grace125 avatar johanhelsing avatar jondolf avatar jpedrick avatar kav avatar leshainc avatar mattdm avatar mbrea-c avatar nisevoid avatar ollef avatar perrypeak avatar ramon-oliveira avatar rigidity avatar rj avatar shaddowspy avatar shanecelis avatar starwolfy avatar teamdman avatar tremorris1999 avatar trustnooneelse avatar waywardmonkeys avatar zentropivity avatar zwazel avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

bevy_xpbd's Issues

Slight variation on many_shapes.rs has worse performance?

Hi,
Firstly, great work with this library man! The seamless integration with Bevy is a winner. I know your busy with school work so no rush on this one. But if some thing obvious stands out to you here, please let me know.

I have a weird one. I've copied the many_shapes.rs example, where instead I'm just using block and they appear every 2 seconds at the top of the screen. By the time I get to 10 the whole thing is laggy, which is weird as many_shapes.rs seems to have a lot more ridged bodies + colliders in the scene? I can't figure out what the big difference is here?

Here is the full main.rs file. Simpler than many_shapes.rs really:

use bevy::window::WindowMode;
use bevy::{prelude::*, sprite::MaterialMesh2dBundle};
use bevy_xpbd_2d::{math::*, prelude::*};
use rand::Rng;

#[derive(Resource)]
struct WordSpawnTimer(Timer);

fn main() {
    App::new()
        .add_plugins(DefaultPlugins.set(WindowPlugin {
            primary_window: Some(Window {
                resizable: false,
                mode: WindowMode::BorderlessFullscreen,
                ..default()
            }),
            ..default()
        }))
        .add_plugins(PhysicsPlugins::default())
        .insert_resource(WordSpawnTimer(Timer::from_seconds(
            2.0,
            TimerMode::Repeating,
        )))
        .insert_resource(ClearColor(Color::rgb(0.05, 0.05, 0.1)))
        .insert_resource(SubstepCount(6))
        .insert_resource(Gravity(Vector::NEG_Y * 1000.0))
        .add_systems(Startup, (setup_graphics, setup_scene))
        .add_systems(Update, (spawn_blocks, keyboard_input))
        .run();
}

fn setup_graphics(mut commands: Commands) {
    // Add a camera so we can see the debug-render.
    commands.spawn(Camera2dBundle::default());
}

fn setup_scene(mut commands: Commands, window: Query<&Window>) {
    let window = window.single();

    let width = window.resolution.width();
    let height = window.resolution.height();

    // Floor
    commands.spawn((
        RigidBody::Static,
        Position(Vector::new(0.0, -height / 2.0)),
        Collider::cuboid(width, 10.0),
    ));
    // Left wall
    commands.spawn((
        RigidBody::Static,
        Position(Vector::new(-width / 2.0, 0.0)),
        Collider::cuboid(10.0, height),
    ));
    // Right wall
    commands.spawn((
        RigidBody::Static,
        Position(Vector::new(width / 2.0, 0.0)),
        Collider::cuboid(10.0, height),
    ));
}

fn random_colour() -> Color {
    let mut rng = rand::thread_rng();
    Color::rgb(rng.gen(), rng.gen(), rng.gen())
}

fn spawn_blocks(
    mut commands: Commands,
    time: Res<Time>,
    mut timer: ResMut<WordSpawnTimer>,
    window: Query<&Window>,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<ColorMaterial>>,
) {
    if timer.0.tick(time.delta()).just_finished() {
        // let mut rng = rand::thread_rng();
        let window = window.single();

        let width = window.resolution.width();
        let height = window.resolution.height();
        // let half_width = width / 2.0;
        let dim = width.min(height) / 10.0;

        let square = (
            Collider::cuboid(dim, dim),
            MaterialMesh2dBundle {
                mesh: meshes.add(shape::Box::new(dim, dim, dim).into()).into(),
                material: materials.add(ColorMaterial::from(random_colour())),
                ..default()
            },
        );

        // let position = Vector::new(rng.gen_range(-half_width..half_width), height / 2.0 + dim);
        let position = Vector::new(0.0, height / 2.0);

        commands.spawn((
            square,
            RigidBody::Dynamic,
            Position(position),
            Friction::new(0.001),
        ));
    }
}

fn keyboard_input(
    keys: Res<Input<KeyCode>>,
    mut app_exit_events: ResMut<Events<bevy::app::AppExit>>,
) {
    if keys.just_pressed(KeyCode::Escape) {
        app_exit_events.send(bevy::app::AppExit);
    }
}

I'll have a look over the Johan Helsing video's to see if anything stands out to me.
word

An example for `ExternalForce`.

I'm trying to figure out how to apply a force to the player, but I can't figure out how.

I have roughly something like this:

commands.spawn((
    ...
    RigidBody::Dynamic,
    ExternalForce::default(),
    Player,
));

fn system(mut force_query: Query<&mut ExternalForce, With<Player>>) {
    for mut force in &mut force_query {
        force.apply_force(Vec2::Y * 10.);
    }
}

But doing so have no effect on the player. I can query and mutate the velocity instead and it will work, so I know that my code is not completely broken at least.

`ang_vel.to_radians()` is a footgun

Because AngVel implements Deref<f32>, we will get std::f32::to_radians, which converts from degrees to radians, but AngVel is already in radians...

Not sure how we'd fix this besides removing the derive for Deref from AngVel and the other components... It's a trade-off and we'd lose some nice ergonomics. Just putting this here in case anyone else has a good idea about how to go about it.

Make `PhysicsLoop::accumulator` public?

I'm writing some interpolation code that keeps track of Transforms from the previous physics tick and interpolates between that and the current Transform every regular update, storing it in a separate entity for rendering. Problem is I need access to PhysicsLoop::accumulator, which currently is private (because of pub(crate)). Could this be changed?

Also noticed that the PhysicsLoop can't be found in the docs.

Extremely slow debug code

I am trying to replace rapier engine with xpbd.
Due to very similar syntax I switched from rapier to xpbd in just a few hours, awesome!
However I can not proceed with my game development.
Debug mode cargo run extremely slow. 2 - 10 fps...
2023-08-23_19-13
7 balls + 1 projectile ball + 2 next balls + 2 walls as cuboids + 2 lines as cuboids = that is all
And even for this small amount of entities physics engine is extremely slow.
I am using simple colliders+positions, maybe I am missing something?
In comparison to bevy_rapier - I can run about 30 entities without any lags.

Is there any diagnostic for this engine, so at least I can try to investigate what system cause this?
I can only see that comment out Collider increase my frame rate to 20 fps...

Tips from [Why is performance so bad?](https://docs.rs/bevy_xpbd_2d/latest/bevy_xpbd_2d/#why-is-performance-so-bad) section do not help.

Center of mass behaves incorrectly for off-center colliders

If I make a rigid body with a single capsule collider with an off-center origin, e.g.:

// 1m tall character with origin at (0,0); using Z-up so gravity is (0, 0, -9.8)
SharedShape::capsule(point3(0.0, 0.0, 0.25), point3(0.0, 0.0, 0.75), 0.25),

If I inspect the inferred CenterOfMass component, it correctly shows as CenterOfMass(Vec3(0.0, 0.0, 0.5)). However the object behaves as if the center of mass was at (0, 0, -0.5), so the capsule wobbles upright on one end instead of falling over.

The same thing happens if I make a centered capsule and use a compound collider to offset it:

    let cap = SharedShape::capsule_z(0.25, 0.25);
    let xf = Isometry3::translation(0.0, 0.0, 0.5);
    let c = SharedShape::compound(vec![(xf, cap)]);

[Actually the same thing happens in 2.0. In the latest it fails on a todo in as_support_map for compound.]

Colliders seem to work fine if their center of mass is (0,0,0).

(I haven't made a small repro for this yet but I can.)

Collision Filtering and Modification

Future features in the readme lists Collision Filtering as something not yet implemented and I would like to get the ball rolling if possible.

Topics discussed in the xpbd help channel on Discord

Some topics that were discussed on Discord included:

  1. Global physics hooks that can iterate over all collisions
  2. Per-entity filter components
    • Filtering collisions with entities by EntityId
    • Filtering collisions by predicate
  3. Being able to modify the individual contacts
  4. The possibility of being able to use one-shot systems in the future
  5. The possibility of being able to use entity relations to allow more efficient filtering

Global Physics hooks

Adding a schedule after the SubstepSet::NarrowPhase, and exposing a way to filter collisions from the Collisions resource gives a very open-ended and straightforward way to allow for modifying collisions.
I've got a working example on this fork here.

I'm in two minds about whether it's worth the schedule to have built-in sets (e.g. First, Filter, Modify, Last), or whether that complexity should be left up to the user. I can see a potential want to pre-filter collisions with faster filters before any slower ones execute, for example.

Per-entity filter components

@Jondolf suggested something in the following form:

ContactFilter::new()
        .with_excluded_entities([...])
        .with_predicate(|mut contact: ContactData| {
            // Modify contact (optional)
            contact.penetration = 0.1;
            // Return the contact or use None to ignore contact
            Some(contact)
        }),

It was also suggested that some form of combination rule may be needed to handle when two entities with conflicting filter predicates are present.

Modifying individual contacts

Being able to modify the individual contacts is desirable. Therefore, being able to return Some(contact) from a post-processing step is more useful than only being able to remove or keep collisions.

One-shot systems

@Jondolf showed the following code on Discord as something that it would be nice to be able to do. There was discussion about how this may not be possible with one-shot systems, but using run_system_with* may allow it as long as the overhead was acceptable.

* run_system_with was added to bevy with this merged but unreleased-at-the-time-of-writing pull request.

fn setup(mut commands: Commands, contact_hook_pipeline: ContactHookPipeline) {
    commands.spawn((
        ContactHook::new(
            &contact_hook_pipeline,
            |In(mut contact): In<ContactData>, query: Query<&MyBundle>| {
                // Modify contact, query for entities, do whatever
                // Return new contact data to narrow phase; if None, skip contact
                Some(contact)
            }
        ),
    ));
}

Entity Relations

Something along the lines of (Body1, CollidesWith(Contacts), Body2) was suggested by @Jondolf.

Bevy currently has a tracking issue for entity relations here: bevyengine/bevy#3742

Other notes/questions

Should filtering be treated as a separate issue from modification?

My current personal needs for collision filtering are very simplistic: I just want to implement one-way platforms, which is easily solved by the first bullet point above. I therefore have a very narrow view of what needs exist when it comes to being able to filter/modify collisions, and so would like to get input from other interested parties.

One shortfall of bevy_rapier that I would like to avoid was that it seemed impossible to register more than one set of query params to use with collision hooks because the parameters are registered on the plugin itself as a generic type. Being able to access queries while filtering and modifying collisions seems very important in an ECS.

Where to go from here

I've started work on global filters and entity-entity collision exclusion in a fork here, but am looking for input. I'd be interested in creating a pull request at some point, even if it's only for the global collision hook, as it's a small enough change that gives quite a bit of power from the get-go.

Test failing on Apple M1 due to float discrepancy > epsilon

I have a branch to fix a couple of places where assert_eq is changed to assert_relative_eq to make the tests pass on a mac M1.

This one I want to flag/query though - since it has a specific epsilon, and the difference in expected and observed values is much larger than the epsilon.

If this difference between left and right is within the acceptable range?
I could change the epsilon to make it pass? or change the epsilon just for apple-m1?
Seems weird it's such a large difference.

Anyway, happy to try and get all tests passing on m1 with a bit of guidance. thanks

The failing test:

        assert_relative_eq!(
            mass_props.mass.0,
            original_mass_props.mass.0,
            epsilon = 0.000_001
        );

output from cargo test:

failures:

---- components::world_queries::tests::mass_properties_add_sub_works stdout ----
thread 'components::world_queries::tests::mass_properties_add_sub_works' panicked at 'assert_relative_eq!(mass_props.mass.0, original_mass_props.mass.0, epsilon = 0.000_001)

    left  = 15.642822
    right = 15.6427965

', crates/bevy_xpbd_2d/../../src/components/world_queries.rs:257:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    components::world_queries::tests::mass_properties_add_sub_works

Update to Bevy 0.10

Just creating this to track (haven't started anything).

Stageless should clean up the substep looping quite a bit :)

Panic when removing colliders

Sometimes when removing colliders I get panic at src/plugins/spatial_query/pipeline.rs:135:67:

fn update_incremental_internal(...) {
    // ...

    if refit_and_balance {
        let _ = self.qbvh.refit(0.0, &mut self.workspace, |entity_index| {
            // Construct entity ID
            let generation = self.entity_generations.get(entity_index).map_or(0, |i| *i);
            let entity = utils::entity_from_index_and_gen(*entity_index, generation);
            // Compute and return AABB
            let (iso, shape, _) = self.colliders.get(&entity).unwrap(); // <-- here
            let aabb = shape.get_shape().compute_aabb(iso);
            aabb
        });
        self.qbvh.rebalance(0.0, &mut self.workspace);
    }

    // ...
}

Happens on 6ec8bc4

Child colliders are not handled properly

I have a game with a ship as rigid body with a collider and engines attached to it as children, each has colliders and mass as well. I've enabled debug renderer to see the colliders. Engines' colliders don't move with teh ship, but instead stay at (0, 0, 0). Also, they don't have any rotation or scale applied. I'm not sure if this is a bug with only debug renderer, or handling of hierarchy is not even intended, but this does bother me while I'm transitioning to this engine from rapier as an experiment.

Marching Cubes support and/or examples?

Was wondering if there's any plans to support marching cubes or some other variant of destructible terrain in this engine. Or if that's a bit too high level for xpbd, maybe there could be some sort of example along those lines?

Manually mutate `Rotation`.

Is there a way to manually mutate the Rotation component? Looking at the methods, it looks like that they all just return new structs. Is there a way to actually change the rotation manually?

Check collisions between two entities

How do I check for collisions between two entities? The docs has this:

fn my_system(mut collision_event_reader: EventReader<Collision>) {
    for Collision(contact) in collision_event_reader.iter() {
        println!("{:?} and {:?} are colliding", contact.entity1, contact.entity2);
    }
}

But how do I check between two specific entities? This is the ideal API I am imagining:

fn my_system(
    player_query: Query<Entity, With<Player>>,
    bullet_query: Query<Entity, With<Bullet>>,
) {
    let player = player_query.single();
    for bullet in bullet_query.iter() {
        if let Collision(contact) = check_collision(player, bullet) {
            do_something(contact);
        }
    }
}

Is there a way to do this with this library?

Run sync when simulation paused

Currently, if an object is moved while the simulation is paused, its transform is not updated, so the collision queries for example will not work until the simulation is resumed

Crash related to despawning entity

I have a short lived bullet, when it gets despawned using commands.entity(entity).despawn(); i get a crash:

thread '<unnamed>' panicked at 'called `Option::unwrap()` on a `None` value', /Users/rj/src/bevy_xpbd/crates/bevy_xpbd_2d/../../src/plugins/spatial_query/pipeline.rs:82:59
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Encountered a panic in system `bevy_xpbd_2d::plugins::spatial_query::update_query_pipeline`!
Encountered a panic in exclusive system `bevy_xpbd_2d::plugins::setup::run_physics_schedule`!
Encountered a panic in system `bevy_app::main_schedule::Main::run_main`!
thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: RecvError', /Users/rj/.cargo/registry/src/index.crates.io-6f17d22bba15001f/bevy_render-0.11.0/src/pipelined_rendering.rs:110:77

might need to check in there that the entity still has a collider - i suppose it doesn't if it's being despawned?

not exactly sure what triggers it, but happens pretty soon if i fly around blasting bullets that are set to despawn after a second or so.

Cubes start moving after a complete stop

Cubes start moving after a complete stop, as if they are possessed:

before.mp4

The issue is mitigated by enabling f64 precision.

Note that it happens even when objects are close to (0, 0) and is unrelated to #84: I've tested both with and without the PR, same result.

Default values for friction, restitution and damping should be globally configurable

We should make it possible to configure the default values used for friction and restitution. It's annoying to set each body's friction separately if your world is full of ice, for example.

While we can't change the value returned by e.g. Restitution::default() at runtime, it should be very straightforward to do in the PreparePlugin when initializing missing components for bodies and colliders. We can store the default values in resources and use them for the default values of the components.

Naming

How do we want to name the resources? Do we want each component to have a separate resource, or should we group properties together?

Here are a few ideas:

// All separate
struct DefaultFriction(Friction);
struct DefaultRestitution(Restitution);
struct DefaultLinearDamping(Scalar);
struct DefaultAngularDamping(Scalar);

// Grouped
struct DefaultMaterialProperties {
    friction: Friction,
    restitution: Restitution,
}
struct DefaultDamping {
    linear: Scalar,
    angular: Scalar,
}

// All together
struct DefaultPhysicsProperties {
    friction: Friction,
    restitution: Restitution,
    linear_damping: Scalar,
    angular_damping: Scalar,
}

The Default could also be replaced by Global or World, whichever one is more clear.

Personally, I prefer the first option of using separate resources, since it's a bit more explicit and consistent in terms of our naming conventions, but if others have different ideas, feel free to share.

Licensing and future of xpbd-based physics in bevy

Hi Jondolf, author of bevy_xpbd-the tutorial series here :) fun to see I inspired something and the project still goes on :)

From time to time, I've been toying with the idea of picking up and finishing my tutorial. My plan was to wrap up the friction chapter, clean up the language, and then making my own bevy_xpbd repo public and release on crates.io... which I guess I no longer have to do since you're way ahead of me :P

I'm still wondering if I should finish the tutorial itself, though, and I'm thinking about looking at your code for inspiration :) ...but I'm wondering: would you mind dual-licensing your code under Apache-2 as well? That way anything you/we make can easily be moved between the various crates in the bevy ecosystem.

Also: what's your plan for nalgebra/parry types in public API, is it going to stay or not?

Incorrect time dilation for custom fixed schedules.

Configurations with their own fixed schedule passed to PhysicsPlugins::new() have incorrect time dilation.
In my case I pass Bevy's FixedUpdate schedule that I have configurated to run at 1./16.. This causes physics sim to run in slow-motion. Setting PhysicsTimestep::Fixed in addition does not solve the time dilation problem.
I pass my own FixedUpdate to keep my schedules in sync.

Remove ball bouncing completely

In my game I want to prevent projectile-ball bouncing when it hits other balls in grid.
projectile-ball and grid-ball actually created from the same function and I set Restitution { coefficient: 0.0, combine_rule: CoefficientCombine::Multiply } for all balls.

When I shoot with projectile-ball I set it's LinearVelocity.

Some magnetic force is applying for all grid-balls as ExternalForce

That is all that affected both grid-ball and projectile-ball.

I want my ball to completely stop when they started to collide/touch.
What I see right now is that balls have some kind of Inertia.
For example projectile-ball bounces back (depends on the hit angle):

ball_bounces_back.mp4

Enother example is why my projectile-ball pushes other grid-balls:

ball_pushes_other_balls.mp4

How can I completely stop balls on the first touch?

How do I make things fly ?

I'd like to grave some objects fly, i.e. I'd like to be able to compute their lift, drag, and have everything work like by magic.
Is that something possible ? If yes how would I do that ? 1-entity constraints ?

can you give a load_asset example?

Can you give me an example of loading a third-party model to generate collisions? I loaded a third-party model myself to generate collisions, which became very slow

Add arc/semicircle collider

This is a useful primitive, that can be checked much more quickly (and accurately) than the equivalent polyline or convex hull.

It's also a vital component of slime volleyball!

Update collider verticies

I have a wall creation system and my game and I need to update colliders when player change walls.
Currently I just create new collider trimesh every update. But it would be better if I could replace the old trimesh with a new one to avoid reallocation (from slice, for example).

Sometimes CollidingEntities will accumulate all the collided entities, sometimes not

image

Sometimes CollidingEntities starts behaving incorrectly. Sometimes after the end of a collision with an object, its entity is not removed from the list, resulting in a hang in the air for the player's dynamic controller, as shown below. In case the incorrect behavior of the component has started, it starts accumulating in CollidingEntities all entities it has encountered. Sometimes incorrect behavior is not observed.

Cube controller code:

app.add_systems(PhysicsSchedule, move_player.run_if(in_state(EditorState::Game)).before(PhysicsStepSet::BroadPhase))

...

fn move_player(
    keyboard_input: Res<Input<KeyCode>>,
    mut query: Query<(Entity, &mut LinearVelocity, &mut AngularVelocity, &PlayerController, &CollidingEntities, &mut Transform)>,
    time : Res<Time>
) {
    for (e, mut vel, mut rot, controller, colliding, tranform) in query.iter_mut() {
        if colliding.len() > 0 {
            info!("colliding: {:?}", colliding);
            
            let frw = tranform.forward();
            let up = tranform.up();

            let mut target_vel = Vector::new(0.0, 0.0, 0.0);
            if keyboard_input.pressed(KeyCode::W) {
                target_vel += frw;
            }
            if keyboard_input.pressed(KeyCode::S) {
                target_vel -= frw;
            }
            //rotate
            if keyboard_input.pressed(KeyCode::A) {
                rot.y = 2.0;
            } 
            if keyboard_input.pressed(KeyCode::D) {
                rot.y = -2.0;
            }
            if !keyboard_input.pressed(KeyCode::A) && !keyboard_input.pressed(KeyCode::D) {
                rot.y -= 10.0 * rot.y * time.delta_seconds();
            }
            
            target_vel *= controller.speed;

            if keyboard_input.pressed(KeyCode::Space) {
                target_vel += up * controller.jump_speed;
            }

            //smooth change vel
            let cur_vel = vel.0.clone();
            let cur_vel = vel.0 + (target_vel - cur_vel) * 10.0 * time.delta_seconds();
            vel.0 = cur_vel;

        } else {

        }
    }    
}

Full code can be founded at https://github.com/rewin123/space_editor/blob/v0.1/examples/platformer.rs

Tested on 8b2ea8f in main branch

2D child Z grows without bounds

I made an entity with a position that has a child which also has a position. Both of them have a nonzero z component for draw ordering. That caused the z component of the transform to go off like crazy.

I think this is caused by position_to_transform taking the z coordinate of the child's original transform and treating it as a global transform.

                let new_transform = GlobalTransform::from(
                    Transform::from_translation(pos.as_f32().extend(transform.translation.z))
                        .with_rotation(Quaternion::from(*rot).as_f32()),
                )
                .reparented_to(&GlobalTransform::from(parent_transform));

I changed this to

                let new_transform = GlobalTransform::from(
                    Transform::from_translation(pos.as_f32().extend(transform.translation.z + parent_transform.translation.z))
                        .with_rotation(Quaternion::from(*rot).as_f32()),
                )
                .reparented_to(&GlobalTransform::from(parent_transform));

but I don't know if that still works if the parent is scaled. Maybe it would be best to just set it after doing the GlobalTransform::from?

Way to disable debug render for TriMesh colliders

Static level geometry is often expressed in TriMesh colliders. When the level is big, debug render tries to draw millions of lines using an immediate mode API, which is painfully slow (10 fps)

Other solutions include:

  • drawing a wireframe mesh instead of Gizmos
  • frustum culling
  • per-entity opt-in or opt-out of debug rendering

Unstable collisions

You mentioned in #1 that you were struggling with "unstable collisions", and this was something you'd like to fix before releasing. Just though it might be useful to have an issue for it.

Maybe explaining it and having some extra eyes on it would help :)

(fwiw I was also having trouble with unstable things as I added friction in the tutorial series), and I never quite figured it out, perhaps this is the same thing?

Add `contacts` method to SpatialQuery

Hi ! I'm trying to make my local fork of bevy_mod_wanderlust work with bevy_xpbd and one of the things that's tripping me up is that I can't find any direct parallel for bevy_rapier3d::parry::query::contact. Bevy_mod_wanderlust does some interesting iterative position adjusting in the case they're penetrating the ground already and relies on contact to know which direction to back out from:

if let Ok(Some(contact)) = bevy_rapier3d::parry::query::contact(
    &cast_iso,
    &*shape_caster.raw,
    &ground_iso,
    &*ground_collider.raw,
    0.0,
) {
    // Adjust position so we're no longer contacting
}

bevy_xpbd has a similar SpatialQuery::shape_intersections but it only returns an Entity. I think I could align the entities with the Contacts in the physics world or something but I think they'd be out of date with those from the shape_intersections if you we're to run it multiple times in one frame meanwhile adjusting positions right? That's just my assumption, I'm not sure.

Edit (I'm dumb)

shape_intersections I think still should return Contacts but that function isn't what bevy_rapier3d::parry::query::contact does. shape_intersections returns all entities colliding with that one specific shape. Instead, I think it'd be better to ask for a function to be added to SpatialQuery that returns the contact points between two colliders given their isometries? I might be able to PR this if you'd prefer.

Anyways, very much enjoying the library :D

Test for determinism across systems is failing

It seems that the engine isn't deterministic across machines/OS. For me, the cubes_simulation_is_deterministic_across_machines test used to produce insta snapshots that worked in CI, but I'm guessing that it was because I used to run cargo insta test in Ubuntu, which is the same OS that our GitHub actions use. Now that I'm running it on Windows, the snapshots are different and CI fails.

For now, I think we should remove the test since it's misleading if the engine isn't actually deterministic across operating systems. Eventually it'd be nice to have proper determinism as well.

Manually set mass

I'm messing around with some simple planets and I'm trying to manually set the mass in order to calculate gravitational pull or what not. Right now, I can do something like that:

#[derive(Bundle)]
pub struct PlanetBundle {
    pub planet: Planet,

    pub position: Position,

    pub rigid_body: RigidBody,

    pub mass: Mass,
}

But it's never exactly what I set it to after the first frame or so. For example, when I set a mass of 200.0 it eventually gets changed to 219.31 or something. I remember seeing some code in the lib that added to the mass based off the mass properties so that's probably why.

Is there any way to override the default behavior of the mass changing?

Remove `Pos` and `Rot` in favor of bevy's `Transform`

Currently, bevy_xpbd uses the components Pos and Rot for describing the position and orientation of bodies. At the end of each iteration of the main physics loop, these are synchronized with bevy's Transforms.

I'd like to discuss if we should remove the separate components and move to just using bevy's own Transforms.

Advantages

  • More familiar and convenient for bevy users
  • More complete APIs
  • Reduces code and maintenance overhead (for example, no custom Rot implementations for 2D and 3D, all rotations use Quats)
  • Less components and nicer queries (Transform and PrevTransform instead of Pos, PrevPos, Rot, and PrevRot)
  • No need for synchronization step
  • If/when bevy gets an editor eventually, it would make more sense to manipulate Transforms directly
  • It's what most physics engines seem to do (like bevy_rapier)

Disadvantages

  • Transform hierarchies, scaling and shearing can cause serious problems (see comments below)
  • Parallellism might be harder, since two systems can't access the same Transform at once. Separate systems with Pos and Rot can run simultaneously.
  • No f64 version of Transform unless we make a custom one (adding f64 as a crate feature: #7)
  • In 2D, Pos uses Vec2 and Rot uses a scalar value. Transforms just have a Vec3 and a Quat, which can be considered less ergonomic for 2D. However, bevy users are used to working with them, and they might provide more freedom, so this may not be an issue.
  • Migration pain (not a problem for us now, but could be in the future if this crate becomes more popular)
  • Something else?

Conclusion

Personally, at least from an API standpoint, I think it makes sense to move to using just Transforms, as it would generally be much more convenient and familiar for bevy users, and it would also probably reduce the engine's code and complexity. Using Transforms for physics can cause many problems as well though, which is why I'd like to hear what others think.

Scene colliders

Rapier have AsyncSceneCollider to automatically generate collision after scene spawn.
I would suggest to add a similar feature for convenience, but with the ability to specify collider layers.

Non-convex colliders should use contact manifolds to improve stability

After #90, convex-convex collisions use contact manifolds, i.e. many contact points instead of one. This mitigates pretty much all drifting and explosive behaviour.

However, using the same approach for collisions against non-convex colliders (like trimeshes) caused bodies with convex colliders (like cubes) to sink into the non-convex colliders, sometimes even going straight through. For this reason, they still use the old single contact point method for now.

Fixing this would be really important though, as it would resolve essentially all remaining contact instability that I'm aware of, so let me know if you have any ideas about how to implement it correctly. I'll also look into it more and try to find a fix.

Change RigidBody in system

If it possible to change RigidBody ?
I want to change from RigidBody::Kinematic => RigidBody::Dynamic in some system.
I can not find any example how to do this.

Make `DELTA_TIME` configurable

Okay, so after #4 the algorithm logic got a lot less awkward, and that opens up some possibilities. Currently DELTA_TIME is hard-coded to 60 Hz, but it should probably be configurable at some level.

The obvious thing would be to just make it a resource:

pub struct PhysicsTimeStep(pub f32)

However I'm wondering, would that hurt performance? Does the rust compiler precompute constants, that means we shouldn't do this?

If so, perhaps we should consider if we can somehow use const-generics to still make it configurable at build-time (at least at an internal level).

Second question is: would it make sense to also allow a variable timestep? i.e. consume the remainder of the accumulator with one not-whole physics step.

Would obviously not be deterministic, but could perhaps produce smoother visuals if you don't do smoothing in your renderer.

`init_rigid_bodies` should run after `TransformPropagate`

init_rigid_bodies should run after TransformPropagate, since it uses GlobalTransform when Position is missing.

Otherwise newly spawned entities fall back to (0, 0, 0) position.

This would mean that new entities would start moving according to physics only on the next frame. To mitigate that, transform propagation could be run twice โ€” before and after the physics. The 'before' system can have a condition to run only if there are added rigid bodies.

Fixing this should make child colliders possible (at least for static bodies).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.