#Avian Physics

1 messages ยท Page 34 of 1

visual sparrow
#

Oh yeah I donโ€™t doubt that, I'm just a bit sad about the boilerplate

vestal minnow
#

If we split sensor events and exclude body-less colliders from collision detection, we could remove it

#

Since then every collider that gets the event is guaranteed to be attached to a rigid body

visual sparrow
vestal minnow
#

Yes

visual sparrow
#

That would be neat ๐Ÿ‘€

#

Potentially really, really neat

clear dew
#

Is there a usecase to determine what the overlapping volume was at collision before the colliders are no longer intersecting for this? or no

edit: I guess not...but then I personally don't see the use case, but I definitely want to know how people would use this in practice so I can be enlightened :)

cinder summit
#

It could also just be the target of an event rather than the rigid body, and do that unwrap_or to the entity itself internally ๐Ÿค”

clear dew
#

oh crap, another thing...shape casting won't trigger this event right?

vestal minnow
#

no, it won't

clear dew
#

thanks ๐Ÿ˜… sorry if I'm asking nub questions/derailing, still figuring things out even after a few weeks hehe. I think though that if the use case here is excluding a single Query<&RigidBody> whilst still requiring the same amount of code to check for Some as opposed to query.single(trigger.target()), my naive take is that it might not worth IF there is a performance cost to including this information

#

I think the second part of this has to do with sensors as you mentioned as the alternative...as far as I've used sensors, these new collision events essentially succeed Sensor. There's a lot of things you cannot do with Sensors, but now that we have these new observers, I don't quite see where sensors even fit in at this point other than not interacting with a collider via parry...but even then, I've found that using Dominance with a step height less than my tnua float height with a mask is advantageous in this regard (dynamic rigid body sensors) Also naive, so definitely grain of salt take

vestal minnow
# clear dew thanks ๐Ÿ˜… sorry if I'm asking nub questions/derailing, still figuring things ou...

old

.observe(|trigger: Trigger<OnCollisionStart>, rb_query: Query<&ColliderOf>, name_query: Query<&Name>| {
    let target = trigger.target();
    let Ok(ColliderOf { rigid_body }) = rb_query.get(trigger.0) else {
        return;
    };
    if let Ok(rb_name) = name_query.get(rigid_body) {
        println!("{target} started colliding with {rb_name}");
    }
});

new

.observe(|trigger: Trigger<OnCollisionStart>, name_query: Query<&Name>| {
    let target = trigger.target();
    let Some(rigid_body) = trigger.rigid_body else {
        return;
    };
    if let Ok(rb_name) = name_query.get(rigid_body) {
        println!("{target} started colliding with {rb_name}");
    }
});

or ideally, if we get rid of the Option

.observe(|trigger: Trigger<OnCollisionStart>, name_query: Query<&Name>| {
    let target = trigger.target();
    if let Ok(rb_name) = name_query.get(trigger.rigid_body) {
        println!("{target} started colliding with {rb_name}");
    }
});
#

the "new" version could also technically be condensed with map but I wrote out the whole let-else to make the comparison more fair

vestal minnow
#

Similar to Area2D/Area3D in Godot or triggers in Unity

clear dew
#

Oh I see that discrepency. That the collider event includes the rb of the entity the observer collided with, not the rb of the observer

#

even so...I've done this

fn(
    trigger: Trigger<OnCollisionEnd>,
    mut pong: Query<&RigidBody>
) {
   let rb = pong.get(trigger.target()).unwrap();
       warn!("Collision {rb:?}");
}

but I guess an ancestor is added? and the same info can be determined by trigger.event().0 i thought.

vestal minnow
#

That gets the RigidBody of the entity that the event was triggered for, but does not work if the collider is a child collider

clear dew
#

totally makes sense...and I think you've just inadvertantly solved a bug I was dealing with

edit: I have literally been recursing through parent entities instead of using ColliderOf that's wild hahaha

#

I am down for this

vestal minnow
#

The main problem I still have with this is that it's not clear from the names that it's the collider and rigid body of the other entity, not the entity the event was triggered for

#

I guess we could call them other_collider and other_body but idk if that's weird

clear dew
#

Well...hopefully I'll be able to contribute something more insightful as I keep working with it ๐Ÿ˜… ...if there's one thing I'll say about avian, it's that the entire reason I've decided to use it has never been about performance (despite me mentioning it minutes ago), nor necessarily capability. It's been about accessibility. That an ECS plugin's primary offer is "organized chaos." The primary draw for me is that discoverability is particularly easy with Avian. My reasoning is probably not what most people look for, but I would definitely appreciate this given that sensor triggers + this result in a mutually exclusive, collectively exhaustive set of use cases
tl;dr hell yeah

vestal minnow
#

Hmmm another minor naming thing, maybe we should use body rather than rigid_body for properties like this

#

The rigid_ qualifier is kind of unnecessary there and makes things slightly more verbose

visual sparrow
visual sparrow
visual sparrow
vestal minnow
#

I think for now I'll just go with collider and rigid_body, and if people end up being confused, we can consider renaming them

#

and in a follow-up, try a rigid_body -> body rename

vestal minnow
#

diff is nice +81/-113

sleek thicket
#

turn chaotic evil and rename it to rbdy

cinder vapor
#

How can I stop the auto-calculation of center of mass when using ColliderConstructor with TrimeshFromMesh?

vestal minnow
#

imo rb is fine for internal names but not for something we expose as a property to users

vestal minnow
cinder vapor
#

It didn't work. But I'll check again.

#

wait now it works

#

I probably saved the blender file without exporting it

#

Kinematic rigidbodies are not affected by damping correct?

vestal minnow
#

Correct, though I'm wondering if maybe they should be affected

#

In general engines don't apply damping to kinematic bodies

sleek thicket
vestal minnow
cinder vapor
#

got it. thanks. it seems that's also the case for box2d

visual sparrow
cinder vapor
#

Super Gatcha Ball

#

not to be confused with another simian related game

half ruin
#

@weary hornet @vestal minnow @green adder

Do you guys have any news about KCC impl?
I found the best KCC impl I saw - its in Unity ECS package CharacterController
I tested it, and it works flawlessly - no glitches, stuttering, or freezing at all, even with complex ground shapes and 3D objects (check the video I attached)
Also tried to understand how to translate it to rust/bevy/avian but its too complex for me) (and they use burst decompiled code source as I remember)

https://www.youtube.com/watch?v=W2FJvHLoiqo

vestal minnow
#

One of the methods we're considering for the plane solver is the one used by that Unity KCC

cinder summit
#

Whoa, 16 members? Meanwhile the number of people doing BVH related stuff for avian and determinism is like 3, and the number for solver related stuff is 1 ferris_sob

vestal minnow
#

Idk I guess a lot of people just need a KCC and it's something that doesn't need as much knowledge of internals and can mostly be built externally

half ruin
cinder summit
#

Speaking of BVH ... I have a working raycast against SDFs now!
Gonna have to improve a couple of UX issues and make the trait a bit more flexible for the rest though

visual sparrow
half ruin
visual sparrow
#

Btw, how expensive is computing a convex hull? I'm doing that ~100 times on level spawn for complex meshes and it runs fine on my machine, I'm just worried it might be bad for others with lower specs.

#

Asking because I have noticed that spawning a level takes quite a while on Wasm on other machines and this may be a culprit

#

Though I didnโ€™t do any tracing yet

#

I also know that glTFs take long to process on Wasm in general, so that might also be the cause

vestal minnow
#

Waaay cheaper than convex decomposition but not like super cheap

cinder summit
#

Asset processing for scenes when ferris_sob

cinder summit
calm sundial
#

gltf physics support would really help with this.

#

still not ratified.

visual sparrow
vestal minnow
#

It'd be nice if we had something like #ecosystem-working-groups for collaborating on ecosystem crates. I recall asking for that at one point, but alas...

#

Or alternatively just allowing popular crates to have working groups in #1235758970703188008

visual sparrow
visual sparrow
vestal minnow
#

Yeah convex decomp is insanely expensive to run for larger meshes but generally produces faster colliders. It'd really benefit from asset preprocessing

#

Constructing a trimesh collider is basically free in comparison

visual sparrow
#

Since in my case, itโ€™s not 100 distinct meshes, but like 10 meshes each 10-15 times

vestal minnow
#

Maybe ๐Ÿค” This feels like something we'll really want a first-party solution for but we might want asset preprocessing stuff first

visual sparrow
#

If we can get one of the three people on earth who understand it to implement it

vestal minnow
#

the real-time visualization is cool

calm sundial
#

Hmm. How Avian is going to handle asset streaming when it's implemented in Bevy? There was a discussion recently in the assets channel.
To be efficient colliders, rigid bodies and related observers should not be all loaded and active together and should be loaded on demand. Again with gltf physics this will be easier to impl.
But it's not just Avians components. All components that aren't needed at the moment should not be loaded and active so maybe Avian wound not even need to add support for this and it will be working out of the box.

vestal minnow
#

It's not something I've thought about too much yet since asset stuff in Bevy itself is kinda half-baked (at least in terms of UX and docs) and huge worlds haven't been relevant yet. But mainly I think we'd just need it to be fast to insert and remove batches of bodies, ideally doing any costly prep work on some background thread without blocking the main thread

calm sundial
#

probably all custom components added to an asset would be readded when asset is reloaded

visual sparrow
#

@vestal minnow another thing: is there a builtin way to find out whether a rigid body has come to rest or has started moving again? I assume that kind of check is necessary for sleep as well.
Asking because this is relevant for navigation. Resting bodies should affect the nav mesh, moving bodies should rely on local avoidance.

vestal minnow
vestal minnow
vestal minnow
visual sparrow
vestal minnow
#

Now I intend to be primarily in release note writing mode to finally get the 0.3 release out soon, I have some of it written already but still have a lot to do

#

thankfully for most of it I can largely copy PR descriptions

golden python
#

Hello, I am curious what would be the easiest way to filter out sensors when doing a spatial query ?

cinder summit
urban flume
#

I've been putting that off while working on other things. Jondolf talked about adding a KCC to avian but it's not a priority afaik

vestal minnow
#

But yes it's more of a collective effort than my personal priority for now; for 0.4 I'll likely be more focused on solver internals and some of the other large reworks we have going on (broad phase rework, spatial query rework, force API rework, etc.) but I will still be also following the KCC working group and helping out where needed

#

It's cool that we have something more collaborative that people are working on together rather than it being a single person (usually me) implementing the whole thing ๐Ÿ˜…

calm belfry
#

I'm working on a pinball-esq game and tried adding a ramp made using convex decomposition. Instead of rolling up the ramp the ball is popping up. Any ideas on what I can tune to fix this?

sleek thicket
calm belfry
final relic
#

Why is there no newer version of Avian published on crates.io since January?

Stuggled for about 30minutes - 1hr thinking 0.2.1 was the latest.

And I don't like connecting with the github repo directly, seems safer to stick with a published version

cinder summit
#

Iirc Jondolf was talking about making release notes for the new release, so I assume a new release should be getting close ๐Ÿค”

little maple
visual sparrow
final relic
jolly rover
hard helm
#

Should scale O(n log n), although QuickHull (used by Parry IIRC) has a worst case behavior of O(n^2), but only for pathological cases.

#

Like quick sort.

visual sparrow
little maple
#

I'm seeing propagate_transforms_physics vary in duration from 213ยตs to 2.1ms while I have physics time paused.. is that unusual?

vestal minnow
#

I recall someone else mentioning something similar too, it's unexpected yeah

little maple
#

it may have been me a few days ago ๐Ÿ‘€ I did a little more analysis of my traces

vestal minnow
#

unexpected as in "I'm not sure what's happening there, but it's very possible it's a problem in Avian?"

vestal minnow
little maple
jolly rover
#

how can I avoid this panic?

    /// Creates a [`Rotation`] from the sine and cosine of an angle in radians.
    ///
    /// The rotation is only valid if `sin * sin + cos * cos == 1.0`.
    ///
    /// # Panics
    ///
    /// Panics if `sin * sin + cos * cos != 1.0` when the `glam_assert` feature is enabled.
    #[inline]
    pub fn from_sin_cos(sin: Scalar, cos: Scalar) -> Self {
        let rotation = Self { sin, cos };
        debug_assert!(
            rotation.is_normalized(),
            "the given sine and cosine produce an invalid rotation"
        );
        rotation
    }

๐Ÿ‘€

when the glam_assert feature is enabled.

thread 'main' panicked at /home/current_user/.cargo/git/checkouts/avian-5a22c167119f3550/c505c9d/crates/avian2d/../../src/position.rs:254:9:
the given sine and cosine produce an invalid rotation
stack backtrace:
   0: __rustc::rust_begin_unwind
             at /rustc/2da29dbe8fe23df1c7c4ab1d8740ca3c32b15526/library/std/src/panicking.rs:697:5
   1: core::panicking::panic_fmt
             at /rustc/2da29dbe8fe23df1c7c4ab1d8740ca3c32b15526/library/core/src/panicking.rs:75:14
   2: avian2d::dynamics::integrator::semi_implicit_euler::integrate_position
   3: avian2d::dynamics::integrator::integrate_positions::{{closure}}
             at /home/current_user/.cargo/git/checkouts/avian-5a22c167119f3550/c505c9d/crates/avian2d/../../src/dynamics/integrator/mod.rs:301:13

I tried to force its hand to no avail with:

pub fn plugin(app: &mut App) {
    app.add_systems(
        PhysicsSchedule,
        normalize_rotations
            .before(IntegrationSet::Position)
            .after(IntegrationSet::Velocity)
            // not sure why this is needed when we are being fairly specific in scheduling?
            .ambiguous_with_all(),
    );
}

fn normalize_rotations(time: Res<Time>, mut q: Query<(Entity, &mut Rotation, &AngularVelocity)>) {
    let delta_seconds = time.delta_secs();
    for (entity, mut rot, angular_velocity) in &mut q {
        println!(
            "[A] normalize_rotations rotation: {rot:?}, angular_velocity: {angular_velocity:?}, normalized: {}",
            rot.is_normalized()
        );
        *rot = rot.normalize();
        // let _ = Rotation::radians(angular_velocity.0 * delta_seconds);
        println!(
            "[B] normalize_rotations rotation: {rot:?}, angular_velocity: {angular_velocity:?}, normalized: {}",
            rot.is_normalized()
        );
    }
}
vestal minnow
#

when the glam_assert feature is enabled.
whoops that's wrong, I must've originally copied glam's docs or something ๐Ÿค”

#

it's meant to be "when debug_assertions are enabled."

#

since this is a debug_assert, you won't get it in release mode or if you've disabled debug assertions

#

I haven't seen this panic in a while though, if it's happening during position integration then I think the from_sin_cos call that is crashing is in either Rotation::radians or Rotation::fast_renormalize

cinder summit
vestal minnow
jolly rover
vestal minnow
jolly rover
#

like if I did in fact have a wonky value I'd assume Rotation::radians(angular_velocity.0 * delta_seconds); would be enough to trigger it based on the code for the various systems I'm looking at through the steps

jolly rover
#

ah, running a modified avian2d crate locally I can see radians param in

// impl Rotation
pub fn radians(radians: Scalar) -> Self

is NaN for my reproduction case.

which going further back, ang_vel in integrate_position is NaN

trying to figure out where the NaN comes from now ferrisThink

viral goblet
jolly rover
jolly rover
candid niche
#

I've heard that there is currently an issue with speculative collision/ccd and that it isn't currently fully deterministic, is this the case?

jolly rover
# jolly rover <:ferrisThink:379064048051814400> ```rust ContactManifold { ...
Index: src/collision/collider/parry/contact_query.rs
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/collision/collider/parry/contact_query.rs b/src/collision/collider/parry/contact_query.rs
--- a/src/collision/collider/parry/contact_query.rs    (revision Staged)
+++ b/src/collision/collider/parry/contact_query.rs    (date 1746571987828)
@@ -16,7 +16,7 @@
 
 use crate::{collision::contact_types::SingleContact, prelude::*};
 use bevy::prelude::*;
-use parry::query::{PersistentQueryDispatcher, ShapeCastOptions, Unsupported};
+use parry::query::{PersistentQueryDispatcher, ShapeCastOptions, TrackedContact, Unsupported};
 
 /// An error indicating that a [contact query](self) is not supported for one of the [`Collider`] shapes.
 pub type UnsupportedShape = Unsupported;
@@ -235,7 +235,21 @@
             return None;
         }
 
-        let points = manifold.contacts().iter().map(|contact| {
+        fn is_invalid_contact(contact: &&TrackedContact<()>) -> bool {
+            contact.dist == -1.0 && (contact.local_p1.x.is_nan() || contact.local_p1.y.is_nan() || contact.local_p2.x.is_nan() || contact.local_p2.y.is_nan())
+        }
+        
+        fn is_valid_contact(contact: &&TrackedContact<()>) -> bool {
+            !is_invalid_contact(contact)
+        }
+ 
+        let valid_contacts = manifold.contacts().iter().filter(is_valid_contact).collect::<Vec<_>>();
+        
+        if valid_contacts.is_empty() {
+            return None;
+        }
+
+        let points = valid_contacts.into_iter().map(|contact| {
             ContactPoint::new(
                 subpos1.transform_point(&contact.local_p1).into(),
                 subpos2.transform_point(&contact.local_p2).into(),

I still don't know if this a result of me doing something dumb or a parry issue, but this is what fixed it for me. I tried creating a cleanroom reproduction case but I can't seem to figure out exactly what behavior is triggering it. I'm fracturing a mesh into multiple meshes so maybe the collider that gets generated with it has some oddities from a bad mesh?

jolly rover
glossy spoke
#

Hola! A question regarding FixedJoint.
I'm trying to mount a rotated thingie onto another thingie, does FixedJoint try to align rotations?
I want that triangle to be mounted as on the pic, so it retains it's rotation of -P/2 relative to that rectangle.
Presently the whole construct kinda explodes

ionic fjord
#

Hi!

Is there a way to detect collisions between objects without them actually colliding? Say I want a bullet to go straight through an enemy without physics interaction, but I still want to see if the bullet pierced the enemy.

glossy spoke
ionic fjord
glossy spoke
# glossy spoke Hola! A question regarding FixedJoint. I'm trying to mount a rotated thingie ont...

I kinda made it work. For a simple case of "mount rotated thrusters onto a ship". Looks a bit hacky, I guess will break in other cases.
For those interested:

  1. Rotations are indeed getting aligned for a FixedJoint. But we could calc rotation between two bodies and tell avian to maintain that rotation (instead of 0 rotation). Here's a hacky enhancement to FixedJoint that'll do that.
  2. Seems, local_anchors are treated as created in frame of Rotation 0 (which makes sense, btw). (pic) I.e., you need to find point of contact of rotated entity, and then rotate that point to Rotation 0 - that'll be local anchor..
    And whoop, thrusters are working!
glossy spoke
dreamy viper
#

Hi would it be possible to implement Ease (from bevy) for the various avian components?

#

I can help with that if you agree that it would be useful

dreamy viper
#

Also is anyone using RustRover? I can't seem to get RustRover to import avian correctly. I keep getting 'unused import' errors even though it works with cargo

royal belfry
dreamy viper
#

Thanks, will try. But you're able to use avian main branch using rust rover?

royal belfry
#

I haven't tried it, but this is a broader issue with RustRover and not Avian specific

#

I got it yesterday when trying to use num-traits

dreamy viper
vestal minnow
#

and can't you ease the internal values instead or is there a reason the component itself needs to be eased

dreamy viper
#

The main reason is that I changed my APIs (for correction/interpolation) to automatically do linear interpolation for structs that implement Ease. Plus I think it's better to standardize on the traits by bevy::math

#

I can probably find workarounds where I can ease the internal value; but since those components are kind of omnipresent and are often themselve eased, I think it would be convenient to implement the trait directly on them

vestal minnow
#

I'm not sure how interpolation works for correction but are physics components something you necessarily want to interpolate? Since physics normally runs with a fixed timestep anyway, and for stuff like forces the interpolation would kinda be generating non-existent values since you can't know what the real forces would actually be

#

same for velocities kinda

#

I may be misunderstanding what interpolation does in networking tho

dreamy viper
#

I would start with Position/Rotation, not sure if the others are needed

vestal minnow
#

Yeah those are fine, I can make a quick PR

dreamy viper
#

it's ok i can make the PR

royal belfry
scarlet silo
#

Hey everyone, how do I perform a spatial query / raycast without adding a component to an entity?

cinder summit
#

You can use the SpatialQuery system param to query against existing colliders

scarlet silo
#

Thanks @cinder summit, I just figured it out

vestal minnow
#

Okay, release notes are finally done. If all goes well, I'll release 0.3 tomorrow evening

#

(I have an exam at 5 PM tomorrow and I really need to study now bavy)

vestal minnow
#

ty <3

#

preparing all the images and videos and everything for the release notes always takes ages

#

I have some beautiful SVG art for the thumbnail again lol

sleek thicket
#

do you guys think doing 2 overlap queries to make a boolean shape a stupid idea?
e.g. sphere + cuboid to get an arc in front

little maple
#

is it something you're dynamically changing the shape of? Otherwise, you could make a mesh in blender and make a collider from that?

sleek thicket
#

using primitives might be better for expanding AoE though

vestal minnow
clear dew
#

I'm gonna throw a party

#

that is so awesome, congrats!!!

little maple
#

wow, I've been using main for a while now and did not know about all these features

dreamy viper
#

Upon upgrading, my colliders don't collide anymore; I added app.register_required_components::<Collider, CollisionEventsEnabled>(); but maybe I also need something else?

#

Hm looks like the ColliderHierarchyPlugin now needs to be enabld

vestal minnow
#

I updated the migration guide to be a bit more clear here

little maple
#

hmm.. conveyor belts would be tricky if you're using a floating controller, huh ๐Ÿค”

cinder summit
#

Floating character controllers usually already do a ground check for ground velocity and such, so checking if the ground is supposed to give you velocity wouldn't be super hard either I'd imagine ๐Ÿค”

little maple
#

I guess that's true..

#

separate topic: is there a way to constrain rotation of a dynamic rigid body entity on a specific axis? not lock it fully but keep it so that it can rotate along an axis within a certain range?

so far, the approach I thought of was to have an invisible entity and setup a RevoluteJoint with an angle limit?

golden python
#

This is amazing, thanks Jondolf

past escarp
#

Is there a way to set a global physics scale? using 2d, 1px = 1unit by default which leads to things looking/feeling huge and floaty

#

i was just zooming in the ortho camera but its getting less than idea w/ bevy ui

past escarp
sweet sundial
#

with_length_unit tries to make physics work exactly the same with a different internal scale, what you might want is Gravity

past escarp
#

yeah i guess that would work, though it would be nice to have a global scale since i assume ill have to manually adjust mass density etc to make forces behave the way i want since the scale is all over

sweet sundial
#

just put it in a const at your crate root

plucky elbow
#

how do i make a inverted collider? aka keep everything inside?

tried a sphere with negative size but that acted like nothing was there
tried with convex hull from mesh but that pushed everything out

sweet sundial
#

twenty halfspaces

plucky elbow
sleek thicket
plucky elbow
sleek thicket
#

yeah, just make trimesh then xD

visual sparrow
errant oar
#

Hi all ๐Ÿ‘‹ is it possible to toggle the Physics Debug Plugin's gizmo render at runtime with a resource or something?

vestal minnow
cinder summit
golden python
#

Hello, It might be a common question, but is there a way to cast a shape and get ALL the colliders inside instead of the first hit ?

vestal minnow
#

shape_hits iirc

golden python
#

thanks a lot

#

maybe using the cast terminology would make the discoverability better, since cast_rays would be it's ray analog

vestal minnow
#

the current ray version for multiple hits is also ray_hits so it's consistent between ray cats and shape casts

#

But yeah the names are not ideal

#

We could do cast_ray (many hits) and cast_ray_closest (single hit)

vestal minnow
visual sparrow
prime turret
#

I'm running into a weird issue where once there's been a collision, the rigid body ceases responding to any external forces or torques, including gravity, and I'm looking for ideas for how to debug that.

#

the setup is a large static RB acting as the ground, and a dynamic RB as a vehicle doing raycasts at the ground. If the vehicle flips and contacts the ground, it then just floats there not responding to any more forces or torques.

plucky elbow
#

#1370535365311598623 message

gaunt loom
#

I've tried upgrading to Avian2d 0.3 and I'm seeing new collisions between one body with:

(
    RigidBody::Kinematic,
    Sensor,
    CollisionEventsEnabled,
    CollisionLayers::new(
        GamePhysicsLayer::Projectile,
        [GamePhysicsLayer::Default, GamePhysicsLayer::Enemy],
    ),
)

and another with:

(
    RigidBody::Kinematic,
    Sensor,
    CollisionEventsEnabled,
    CollisionLayers::new(GamePhysicsLayer::Query, [GamePhysicsLayer::Interact]),
)
#

Is this expected?

#

The only thing I can think of is that the second body is attached to my Player's Kinematic body

#

(as in parent-child attached)

jolly rover
#

I could be wrong but I thought child colliders propagate to the parent?

gaunt loom
#

Even if there's an intermediate (kinematic) rigidbody?

#

This is also different to the 0.2 behavior, where Sensors seemed to act as their own thing

#

Might be time for me to set up an MRE lol

jolly rover
jolly rover
#

Does anyone know if there's a way to have a separate entity using the same collider shape propagate forces to its twin? I was originally using a ColliderOf which kind of works but when you turn on the debug plugin you can see the collider shape is attached to the main one which causes problems lol. Unless of course the debug plugin is wrong about it's true position.

My use case is having rigid bodies that are half exposed on one side of the screen have their remaining half wrapped around extruding from the other side (like a portal)

sweet sundial
#

collision hooks on both that insert collisions on the other?

jolly rover
jolly rover
#

This almost works lol. It at least keeps the collision debug wireframes where I'd want them to be.

fn plugin(app: &mut App) {
    app.add_systems(
        PhysicsSchedule,
        update_collision_transform
            .in_set(PrepareSet::Finalize)
            .ambiguous_with_all(),
    );
    app.add_systems(
        PhysicsSchedule,
        update_collision_transform
            .after(NarrowPhaseSet::First)
            .before(NarrowPhaseSet::Update)
            .ambiguous_with_all(),
    );
}

fn update_collision_transform(
    global_transforms_q: Query<&GlobalTransform>,
    mut collider_of_q: Query<(Entity, Mut<ColliderTransform>, &ColliderOf)>,
) {
    for (entity, mut collider_transform, collider_of) in &mut collider_of_q {
        let target_entity = collider_of.body;
        let Ok(global_transform) = global_transforms_q.get(entity) else {
            error_once!("{entity} has no global transform");
            continue;
        };
        let Ok(target_global_transform) = global_transforms_q.get(target_entity) else {
            error_once!("{entity} ColliderOf({target_entity}) has no global transform");
            continue;
        };
        let expected_transform = global_transform.reparented_to(target_global_transform);
        collider_transform.translation = expected_transform.translation.truncate();
        collider_transform.rotation = avian2d::position::Rotation::from(expected_transform.rotation);
        collider_transform.scale = expected_transform.scale.truncate();
    }
}
visual sparrow
#

@vestal minnow is it possible that Avian is not re-exporting the system set in which it's doing interpolation?

#

Asking because I want to place something in AfterFixedMainLoop and order it relative to the interpolation

vestal minnow
visual sparrow
vestal minnow
#

Yeah it looks like it's not currently re-exported from Avian

vestal minnow
#

Yup that'd be nice ๐Ÿ™‚

#

the other re-exports are in src/interpolation.rs iirc so it'd go there

visual sparrow
#

I always feel so guilty when I completely delete that nice PR template you made ๐Ÿ˜„

vestal minnow
#

copied from Bevy

#

I should probably update it though, Bevy's has changed quite a bit

#

I almost never use the Changelog section lol

visual sparrow
#

I see ๐Ÿ˜„

#

Ah wait, changelog, not migration

#

I was about to say, that sounds wrong ๐Ÿ˜„

#

BTW, have you looked into nextest?

#

Seeing as your CI takes quite a while for tests, I'd be curious about the speedups

vestal minnow
#

I haven't, no

visual sparrow
vestal minnow
#

I have no clue how it works but sure ๐Ÿ˜„ would be interesting to see how it compares

#

Has Bevy ever considered trying nextest? This is somehow the first time I've heard of it

visual sparrow
#

Just noticed you don't run doc tests

#

I'll add that as well while I'm on it

vestal minnow
#

pretty sure it is running those

visual sparrow
visual sparrow
#

So I'd have to add that line anyways

vestal minnow
#

I've at least had CI fail on docs many times

visual sparrow
#

IIRC you only get compilation errors without --doc

vestal minnow
#

these are all (or mostly) doc tests

visual sparrow
#

Oh, while I'm on it

#

# TODO: Add ubuntu-latest back. It had CI storage issues, so it's left out for now.

#

We had this on bevy_new_2d and were able to make the test binaries much smaller

vestal minnow
#

ah cool

visual sparrow
#

I'll add it to the PR

#

also, we only save the cache on main

#

that helps against it becoming huge

#

Interested in that?

#

The only other thing that we then have that avian does not is linting from the Bevy CLI

#

But I suppose those would still cause some failures right now

vestal minnow
vestal minnow
#

yeah I guess that'd be good to fix too then

visual sparrow
#

Cool; then I'm done for the moment ๐Ÿ™‚

#

Until you need help setting up the CLI lints

#

(I'm just assuming that there are some things right now that might need renaming, don't know how accurate that is)

visual sparrow
#

Bad news, looks like there's no cache at all ๐Ÿ˜„

vestal minnow
#

There is a cache sometimes... It works on some PRs but not others lol

visual sparrow
#

๐Ÿ‘€

#

Let me fix that for you real quick as well

#

(I had to deal with this recently)

vestal minnow
visual sparrow
#

There you go, fixed your cache misses as well. This will invalidate the caches for this CI run, so we will only see the effects once it's merged and completely ran through once on main

#

If you merge it, I can check back in an hour or so when everything ran through and see if the performance is indeed better

visual sparrow
#

Baah running into the good old issue of the linux runner having no GPU

#

let's fix that too

visual sparrow
visual sparrow
#

@vestal minnow good news: I think the tests should run on GPU-less linux machines again

#

bad news:

#

๐Ÿคทโ€โ™‚๏ธ

#

Is there a possibility that something in there is actually behaving differently than expected?

#

oh wait, we are probably not hitting SceneReady hmm

#

yep

#

I'm probably just in the wrong directory or something

vestal minnow
visual sparrow
#

I'll just disable linux for that one

visual sparrow
#

I failed to get the test working on Linux. The issue is that some plugin is needed to load the scene, and I don't know which

#

Best thing I came up with was

        app.add_plugins((
            MinimalPlugins,
            AssetPlugin::default(),
            ScenePlugin,
            TransformPlugin,
            MeshPlugin,
            GltfPlugin::default(),
            ImagePlugin::default(),
            PhysicsPlugins::default(),
        ));
#

But that apparently wasn't enough

#

So not that test is just ignored on that platform

#

Not a regression, since we didn't run anything on Linux before anyways

vestal minnow
#

Yeah that's fine for now

visual sparrow
#

Added a description of what I did in the PR

#

FYI you'll have to add the linux tests back to the required checks after merging

vestal minnow
#

now Windows is failing ๐Ÿ™ƒ

#

I think it just doesn't allow that syntax for env variables?

visual sparrow
#

maybe it works without that call anyways, let's check

#

(it's needed for when something in your tests is using dynamic linking)

#

(e.g. an example)

#

otherwise I'll just split that into two steps

#

see you in 30 minutes ๐Ÿ‘‹

vestal minnow
#

Also wow this is interesting. My initial implementation for the SolverBody stuff stored a SolverBodyIndex component on each entity, and stored the actual solver bodies in a Vec<SolverBody> stored in a SolverBodies resource. But I decided I should also try storing the SolverBody directly as a component on each entity, and the performance is closer than I expected.

Left: main branch, no SolverBody, querying for RigidBodyQueryItem in the solver
Middle: SolverBody in resource with SolverBodyIndex component on each entity
Right: SolverBody component directly on the rigid body entities

#

So moving from vec accesses with a resource to querying for SolverBody is only about a 1.3% perf loss in this case if my math is correct

#

This removes quite a bit of bookkeeping and extra lookups

cinder summit
#

Doesn't sound particularly statistically significant ... Cache wise these behave largely the same

vestal minnow
#

I feel like it probably makes sense to start with storing SolverBody as a component here, since it removes a lot of the complexity and allows you to just use Entity rather than SolverBodyIndex everywhere, plus it just integrates nicer with the ECS

#

the perf gain of the resource approach I started with doesn't seem meaningful enough to justify the complexity at this stage, though we can always try it again later when we've cut down other bottlenecks further

#

it's interesting just how big the perf difference is from using better optimized query data though :P

visual sparrow
#

looks like the CI is coming through ๐Ÿ™‚
I could also add optimizations that use rust nightly, the mold linker, cranelift, etc.
But that wouldn't help us at all since Windows is the bottleneck and none of these help that much on that platform ๐Ÿ˜…

vestal minnow
#

@visual sparrow Merged!

visual sparrow
vestal minnow
visual sparrow
#

This should make it a bit more faster and eliminate a little issue

#

But so far, we went from 35 minutes down to 14 ๐ŸŽ‰

#

(Note that the runs after that invalidate the cache again)

vestal minnow
#

hmm I'm a bit iffy on including the lockfile but we can try it and see how it plays out

visual sparrow
vestal minnow
#

I don't want it to pollute diffs but I guess it won't generally have too many changes after the initial commit

visual sparrow
#

The only thing that I found legitimately annoying is when two PRs touch the deps

#

Then they get a merge conflict

#

The solution is of course trivial (just take the other lockfile and run cargo check to add your dep on top)

#

But it's still annoying

#

fortunately it hasn't come up often yet

#

@vestal minnow Could you ping me on merge again so I can validate that everything works?

vestal minnow
#

oh it's the benches

visual sparrow
#

I guess we can exclude them from the tests

#

I'll still let clippy run over them, though

visual sparrow
vestal minnow
visual sparrow
#

No problem ๐Ÿ™‚

#

phahahahahaha

manic swallow
#

I got a question about Avian Joints (I'm new to Avian, Bevy and 3D in general) (also let me know if this is not the right place to be asking these kinds of questions).

I've created a Cuboid and a Cylinder as seen on the first picture. Normally cylinders are elongated on the Y axis when created, so I had to rotate it on its Z axis to achieve the results in the picture. All good there so far.

I want to place a PrismaticJoint between the two and set its "Free Axis" to the Y axis, like this:

PrismaticJoint::new(cuboid_entity, cylinder_entity)
    .with_free_axis(Vec3::Y)

My expectation is that I would be able to move the Cylinder up and down on its current form (lying down). However, the moment I place the Joint, the cylinder flips back to its original orientation (with the Y axis point up) like in the second picture.

Am I doing something wrong? Or are my expectations on the usage of Joints wrong?

visual sparrow
vestal minnow
# manic swallow I got a question about Avian Joints (I'm new to Avian, Bevy and 3D in general) (...

Right now the way it works is that it aligns the orientations of the bodies, and doesn't allow any relative rotation. You can see how it forces the red, green, and blue axes to be aligned. Hopefully in the next release we'll have a way to properly set a "local frame" for each body so that you could offset the relative translation and rotation. Ideally this will also be set automatically by default, using the transforms of the entities when the joint is created

#

For now you can kind of cheat this by making the mesh and collider a child of the rigid body entity, and rotating that rather than the actual rigid body

manic swallow
manic swallow
visual sparrow
#

after this, I'm happy with the CI for now ๐Ÿ™‚

gaunt loom
vestal minnow
vestal minnow
#

I'll probably work a bit more on the PR description and polish the implementation, but wanted to get the PR out finally

visual sparrow
#

@vestal minnow great CI news!
https://github.com/Jondolf/avian/pull/731, which causes a total rebuild for avian but no dependencies, takes 14 minutes now, down from 35 minutes ๐Ÿ™‚
I don't know how important the windows tests are for you, but if we disabled them, it would be down to 6 minutes. I could even trim that down to about 4 minutes by changing the linker, compiling with nightly, and using cranelift for the top-level crates.

#

Very happy with these numbers for now!

#

I assume you added Windows for a reason, so I'll leave that for now. But let me know if you don't need it anymore and I'll speed it up further ๐Ÿ™‚

#

Oh, for completeness.

#

Before:

#

After:

vestal minnow
vestal minnow
#

14 min is very workable compared to 34 min ๐Ÿ˜„

visual sparrow
#

Oh wait, I can actually use LLD on Windows

#

let me do another little PR

little maple
vestal minnow
#

It's also optimized for fast conversion to and from SIMD types once we implement that

visual sparrow
somber portal
#

If I understand correctly, creating a projectile weapon with avian is as simple as spawning a dynamic rigid body with a velocity. If so, how do I adjust mass/momentum and bounce?

#

I've been looking at the TF2 grenade launcher as inspiration.

little maple
somber portal
#

Thanks

little maple
jolly rover
#

Is this behavior expected or a fluke? (I'm trying to achieve this in another project but struggling to reproduce it)

https://github.com/StrikeForceZero/avian_playground/blob/remote_rigid_bodies/src/main.rs

    let box_mesh = Rectangle::new(50.0, 50.0).mesh().build();
    let box_collider = convex_collider(&box_mesh).expect("Failed to create box collider");
    let box_handle = meshes.add(box_mesh);

    const DISTANCE: f32 = 200.0;
    let target = commands
        .spawn((
            Mesh2d(box_handle.clone()),
            MeshMaterial2d(material_handle.clone()),
            Transform::from_translation(Vec3::X * DISTANCE),
            box_collider.clone(),
            RigidBody::Dynamic,
            Mass(100.0),
        ))
        .id();

    commands.spawn((
        ColliderOf { body: target },
        Offset(Vec3::Y * DISTANCE + Vec3::NEG_X * DISTANCE),
        Mesh2d(box_handle.clone()),
        MeshMaterial2d(material_handle.clone()),
        Transform::from_translation(Vec3::Y * DISTANCE),
        box_collider.clone(),
        Mass(100.0),
    ));
    app.add_systems(
        PhysicsSchedule,
        update_transforms
            .after(PrepareSet::PropagateTransforms)
            .before(PrepareSet::InitTransforms)
            .ambiguous_with_all(),
    );
fn update_transforms(
    mut query: Query<
        (
            &ColliderOf,
            &Offset,
            &mut Transform,
            &mut Position,
            &mut Rotation,
        ),
        With<Offset>,
    >,
    target_transforms: Query<&Transform, Without<Offset>>,
    target_positions: Query<&Position, Without<Offset>>,
    target_rotations: Query<&Rotation, Without<Offset>>,
) {
    for (&ColliderOf { body }, Offset(offset), mut transform, mut position, mut rotation) in
        query.iter_mut()
    {
        let target_transform = target_transforms
            .get(body)
            .expect("Failed to get target transform");
        let target_position = target_positions
            .get(body)
            .expect("Failed to get target position");
        let target_rotation = target_rotations
            .get(body)
            .expect("Failed to get target rotation");

        *transform = target_transform.with_translation(target_transform.translation + offset);
        position.0 = target_position.0 + offset.truncate();
        *rotation = *target_rotation;
    }
}
GitHub

Contribute to StrikeForceZero/avian_playground development by creating an account on GitHub.

visual sparrow
maiden epoch
visual sparrow
#

Don't forget to add linux to the required checks ๐Ÿ˜‰

vestal minnow
vestal minnow
#

I'm not sure yet how persistent simulation islands and graph coloring will affect networking stuff when we implement them, but I assume it should be fine since they're purely for optimization and shouldn't affect behavior in any way

jolly rover
# jolly rover Is this behavior expected or a fluke? (I'm trying to achieve this in another pro...

isolated it. the main rigid body the ColliderOf points to breaks their collisions when having children.. even just children![] breaks it.

Index: src/main.rs
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/main.rs b/src/main.rs
--- a/src/main.rs    (revision Staged)
+++ b/src/main.rs    (date 1747156318973)
@@ -79,6 +79,7 @@
             box_collider.clone(),
             RigidBody::Dynamic,
             Mass(100.0),
+            children![],
         ))
         .id();
 
jolly rover
vestal minnow
#

I'm not sure I understand what you're trying to do

#

The current expected semantics are that ColliderOf is automatically set to the closest ancestor that is a RigidBody, or the collider entity itself if it is a RigidBody

#

This automatic behavior can be disabled if you disable the ColliderHierarchyPlugin iirc

#

Also if there is no RigidBody ancestor for the collider entity, then I think it should be leaving ColliderOf untouched, meaning you can set it manually

jolly rover
# vestal minnow What exactly breaks here? Collisions work fine in my test case with empty `child...

I have a manual ColliderOf entity that is not a decedant to the RigidBody.
I want it to act like a "remote" collider or a "proxy" collider.
Without intervention, it becomes bound to the target body from ColliderOf - thus it tries to rotate them with the target body as their origin.
But that's solvable with the system I made in one of my snippets above. However the undesired behavior is from the RigidBody triggering update_child_collider_position when the RigidBody has a Children component.

    let target = commands
        .spawn((
            Mesh2d(box_handle.clone()),
            MeshMaterial2d(material_handle.clone()),
            Transform::from_translation(Vec3::X * DISTANCE),
            box_collider.clone(),
            RigidBody::Dynamic,
            Mass(100.0),
            children![], // this triggers `update_child_collider_position` and breaks the proxy collider behavior
        ))
        .id();

    commands.spawn(( // this is a root level spawn, not a child of target
        ColliderOf { body: target }, // this makes it act like a proxy collider
        Offset(Vec3::Y * DISTANCE + Vec3::NEG_X * DISTANCE),
        Mesh2d(box_handle.clone()),
        MeshMaterial2d(material_handle.clone()),
        Transform::from_translation(Vec3::Y * DISTANCE),
        box_collider.clone(),
        Mass(100.0),
    ));

from some testing, this is a potentially naive fix:

Index: src/collision/collider/collider_transform/plugin.rs
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/collision/collider/collider_transform/plugin.rs b/src/collision/collider/collider_transform/plugin.rs
--- a/src/collision/collider/collider_transform/plugin.rs    (revision Staged)
+++ b/src/collision/collider/collider_transform/plugin.rs    (date 1747160427962)
@@ -9,6 +9,7 @@
     prelude::*,
     transform::systems::{mark_dirty_trees, propagate_parent_transforms, sync_simple_transforms},
 };
+use itertools::Itertools;
 
 /// A plugin for propagating and updating transforms for colliders.
 ///
@@ -92,13 +93,25 @@
             &ColliderTransform,
             &mut Position,
             &mut Rotation,
+            Option<&ChildOf>,
             &ColliderOf,
         ),
         Without<RigidBody>,
     >,
-    rb_query: Query<(&Position, &Rotation), (With<RigidBody>, With<Children>)>,
+    parents_query: Query<&ChildOf>,
+    rb_query: Query<(&Position, &Rotation), With<RigidBody>>,
 ) {
-    for (collider_transform, mut position, mut rotation, collider_of) in &mut collider_query {
+    for (collider_transform, mut position, mut rotation, child_of_opt, collider_of) in &mut collider_query {
+        // Skip colliders that are not descendants of the rigid body.
+        {
+            let Some(&ChildOf(parent)) = &child_of_opt else {
+                continue;
+            };
+            let is_descendant = parents_query.iter_ancestors(parent).contains(&collider_of.body);
+            if !is_descendant {
+                continue;
+            }
+        }
         let Ok((rb_pos, rb_rot)) = rb_query.get(collider_of.body) else {
             continue;
         };
vestal minnow
#

Hmm something like that could be fine

#

though iterating over ancestors feels like it could get somewhat costly with deeper hierarchies, but I'm not sure how much we need to worry about that

jolly rover
#

for example, this is how im handling them:

    app.add_systems(
        PhysicsSchedule,
        update_transforms
            .after(PrepareSet::PropagateTransforms)
            .before(PrepareSet::InitTransforms)
            .ambiguous_with_all(),
    );
fn update_transforms(
    mut query: Query<
        (
            &ColliderOf,
            &Offset,
            &mut Transform,
            &mut Position,
            &mut Rotation,
        ),
        With<Offset>,
    >,
    target_transforms: Query<&Transform, Without<Offset>>,
    target_positions: Query<&Position, Without<Offset>>,
    target_rotations: Query<&Rotation, Without<Offset>>,
) {
    for (&ColliderOf { body }, Offset(offset), mut transform, mut position, mut rotation) in
        query.iter_mut()
    {
        let target_transform = target_transforms
            .get(body)
            .expect("Failed to get target transform");
        let target_position = target_positions
            .get(body)
            .expect("Failed to get target position");
        let target_rotation = target_rotations
            .get(body)
            .expect("Failed to get target rotation");

        *transform = target_transform.with_translation(target_transform.translation + offset);
        position.0 = target_position.0 + offset.truncate();
        *rotation = *target_rotation;
    }
}
#

anyway, I can open an issue for all this

frigid rampart
#

Hello everyone,

I thought about posting this here as I'm not sure if this is a bug from avian or from my code.

I have this bug where the bottom of my mesh (blue mesh) sinks in another collider's mesh and flickers randomly.

I think that everything was working as expected with avian 0.1 but starting from 0.2 it started behaving like this, I updated to bevy 0.16 & avian 0.3 and the behavior is still present.

I tried different colliders and tried to make both mesh's colliders thicker but it didn't change the behavior in any way.

any tip on how I can get this fixed?

sleek thicket
#

try making collider a little bit taller

frigid rampart
sleek thicket
frigid rampart
# sleek thicket https://docs.rs/avian3d/latest/avian3d/collision/collider/struct.CollisionMargin...

thanks for your suggestion

I tried this but now the mesh moves randomly on its own

    commands.spawn((
        SceneRoot(asset_server.load(GltfAssetLabel::Scene(0).from_asset("models/shovel.glb"))),
        Transform::from_xyz(4.0, 5.0, 2.0),
        Shovel,
        RigidBody::Dynamic,
        ColliderConstructorHierarchy::new(Some(ColliderConstructor::TrimeshFromMesh)),
        LockedAxes::ROTATION_LOCKED,
        Mass(1.0),
        CenterOfMass(Vec3::new(0.0, 0.0, 0.0)),
        Restitution::new(0.0),
        Friction::new(0.0),
        CollisionMargin(0.1),
    ));

like in here

sleek thicket
#

that's not the first time i see it, but i don't remember how it was solved

frigid rampart
#

setting the margin with higher value results in a weirder behavior, sometimes the mesh sinks in even deeper in the mesh below it.

This is an interesting behavior but would be needed in another game or mechanic not in this current setup, unless I change the street to be a lake or something ๐Ÿ˜‚

frigid rampart
sleek thicket
vestal minnow
#

make sure it's not stuck partially inside the ground with that added margin

#

like position it higher off the ground

#

if these are trimeshes and you're spawning them in an overlapping state, the triangles might get stuck to each other (since trimeshes are hollow)

#

for dynamic bodies it's generally better to use convex hulls or convex decomposition

frigid rampart
frigid rampart
tacit shore
#

Hello folks I'm woking on using physics to do procedural animation, and get some jittery joints, can I get any advice on how to make them "stick together" more. I've tried compliance, force and linear damping, but not sure if I'm missing something (this is for the hips to quads, quads to shins is similar, and the "feet" are fixed)

vestal minnow
#

It's kinda hard to tell what could be causing it

#

I'd probably double-check that the anchor positions are correct, and maybe try increasing the SubstepCount?

#

Would someone with a game project like to briefly try the solver-bodies branch, see if there's a noticeable perf difference, and make sure it hasn't horribly broken anything? ๐Ÿ‘‰ ๐Ÿ‘ˆ

#

I kinda want to just merge it and work on implementing more improvements on top, especially given how early we are in the release cycle

visual sparrow
tacit shore
#

I can test it with this code, is not very intensive, but probably help haha

visual sparrow
#

Mind you, I don't hook into any collision event triggers, so I can't comment on that being broken

#

(Note that the performance in that jittery case goes down by about 20 FPS)

little maple
vestal minnow
#

The perf improvements would mainly be if you had like thousands of contacts, which Foxtrot proobably doesn't

visual sparrow
#

So no ๐Ÿ˜„

visual sparrow
#

Or you could also check it directly, Foxtrot builds out of the box with just cargo run

#

Just need to add a git dep to your branch in the [patch.crates-io] section

vestal minnow
#

Yeah I'll probably merge it tomorrow (or technically today, it's 3:30 AM bavy), just wanted to make sure there isn't anything obvious broken in a more realistic gamey scene considering how much of the solver the PR touches :P

visual sparrow
#

Or maybe you just have eternal darkness in Finland anyways so it makes no difference

little maple
#

I'm getting some before and after metrics

vestal minnow
#

yeah my sleep schedule is broken, at least I don't have to wake up for uni in a solid few months now

#

after this PR I'll probably do the force rework stuff and optimize velocity integration, I think we can get a pretty good perf boost from making gyroscopic torque optional and also trying to pre-compute velocity increments only once per timestep like what Rapier does

#

gyroscopic torque is somewhat expensive

visual sparrow
#

^ pure self-interest, I've never had to deal with performance issues with Avian but regularly run into collisions that parry just does not want to properly handle

vestal minnow
#

I've been gradually porting vero's GJK and EPA implementations to peck, in my benchmarks they were at least 2x as fast as Parry, and they might also give some more robust shape casting

visual sparrow
#

Like the very exotic case of "A still-standing cylinder on a flat trimesh"

vestal minnow
#

vero's stuff is currently 3D-only, but I've already made the core GJK algo dimension-agnostic... just need to add the 2D-specific simplex stuff and implement 2D EPA

little maple
#

it is just the solver-bodies branch, right?

vestal minnow
#

yeah

little maple
#

ok, it's actually kinda tough to get an equal comparison in my game at the moment.

#

left is main, right is solver-bodies

but, I also have more contacts on the right

#

all the physics behaved pretty similarly though

vestal minnow
#

yeah you're not too bottlenecked on the solver either so the perf difference there isn't too big

little maple
#

yeah

vestal minnow
#

in my scene where I get a 3x perf boost I have nearly 13,500 contact pairs :P

little maple
#

my bottle neck does seem to be the narrow phase.. is that kinda expected?

vestal minnow
#

So despite me having so many contacts, the solver is still more expensive in my scene

#

however these are rectangle-rectangle contacts

#

you probably have some more expensive colliders like convex hulls and trimeshes

little maple
#

y..yah. I need to do a simplify pass at some point

vestal minnow
#

or I guess convex hulls should be fairly cheap

#

but ofc not as cheap as 2D rectangles lol

little maple
#

gonna redo my game with 2d rectangles ๐Ÿ‘

visual sparrow
vestal minnow
little maple
visual sparrow
little maple
#

oh, wonder if it would be useful to have counts of collider types on there too? might just be noise at some point..

serene field
#

does this support multiplayer with bevy_ggrs? as it needs to support rollback and be deterministic to be used

visual sparrow
#

But cool UI, will use that next time you ask for performance differences!

vestal minnow
vestal minnow
#

Okay I'm merging the solver body stuff now to unblock some things

vestal minnow
#

damn, it's cool to see the Dzhanibekov effect actually working correctly with gyroscopic torque

#

I need to make a tennis racket on the side as well lol

visual sparrow
vestal minnow
#

I'm optimizing velocity integration rn, I set up this scene to see if we could pre-compute gyroscopic effects only once per time step and not at every substep

#

Like I kinda expected, the answer seems to be no: gyroscopic torque needs an up-to-date orientation and inertia tensor, so using the values from before the substepping loop results in angular momentum not being conserved, which causes rotation to slow down over time

#

I intend to make it opt-in with a marker component like GyroscopicTorque or GyroscopicMotion though, it's cool to support but somewhat expensive and unnecessary for a lot of objects

#

Hmm... technically I could also check if the inertia tensor is close to uniform to avoid unnecessarily computing gyroscopic torque in those cases

little maple
vestal minnow
#

in general, the simpler the colliders are, the better

#

so fewer and bigger I guess

#

trimeshes should also be alright for static geometry though, especially if it's not too detailed / the triangles aren't too small

lyric sorrel
#

I played around with gyroscopic solvers a bit last year, and I found a technique that's more accurate for asymmetric tops (all eigenvalues of inertia tensor distinct) and also a bit faster than the current implementation in my testing, but never got around to doing a PR or anything to show it off

#

Was a couple of bevy/avian versions ago now, but I'd be keen to bring that work up to date when I get the chance if you'd be interested to see it

vestal minnow
#

I'll probably have the PR ready later today

#

My new approach is just using semi-implicit Euler instead of the somewhat expensive implicit Euler solver, but clamping the magnitude of the angular momentum to remain the same to avoid introducing energy into the system

#

I got this idea from how Jolt does it

maiden charm
#

About to try something extremely cursed: incremental migration from Rapier to Avian by using both during the transition period, and migrating systems as I go

vestal minnow
#

I split it out from my other force and velocity integration rework stuff since they're mostly unrelated, but I'll probably make a PR for that tomorrow too

#

I also had a change to avoid computing the gyroscopic torque for shapes with non-uniform inertia tensors, but like... I'm pretty sure that's only spheres, so it's probably not worth it

lyric sorrel
# vestal minnow I got this idea from how Jolt does it

Neat, that looks fairly similar to the approach I took. I noticed the old algorithm lost energy fairly rapidly when doing the Dzhabenikov effect thing, because the angular momentum wasn't being conserved (and in particular would drop by a lot when flipping over) so I tried implementing one of the algorithms in this paper which broadly do:

  • Calculate the angular momentum using the current intertia tensor
  • Estimate average angular velocity over the next step using the gyro term
  • Increment rotation using that average
  • Update the world-frame inertia tensor using the new rotation
  • Use the angular momentum computed before the motion and the new inertia tensor to calculate a "corrected" angular momentum at the end of the step
    Doing it this way exactly conserves angular momentum for a free body, and in my testing it showed pretty good energy conservation properties too (and conserving AM puts a strict upper bound on the rotational energy anyway)
#

(I've been trying to update my old implementation to work on main and something's causing issues, will try and iron it out in the next few days)

lyric sorrel
vestal minnow
lyric sorrel
#

Nah, if an inertia tensor is uniform (i.e. the scaled identity) in one frame it's uniform in every frame

#

I'd expect it to be the same for all the Platonic solids too, anything sufficiently regular + symmetric

vestal minnow
#

Hmm okay

#

the references I found were just saying that gyroscopic effects are for "non-spherical" objects, and intuitively I would think that if you rotate the cube on a non-principal axis, then the tensor will have off-diagonal terms and be non-scalar, and cause the angular momentum and velocity to point in different directions

#

which would then result in gyroscopic torque being applied since the cross product is non-zero

lyric sorrel
#

Which does make it sound like only spheres are spherical tops, even though anything with the right symmetries will be

somber portal
#

Hey everyone. I'm testing out implementing a grenade projectile, and I'm having some trouble. I think I'm somehow spawning the grenade capsule inside another collider, but I can't tell, nor figure out why. Here's the relevant code:

pub fn shoot_grenade(
    mut commands: Commands,
    mut shoot_events: EventReader<ShootEvent>,
    mut damage_event: EventWriter<DamageEvent>,
    player_entity_query: Query<Entity, With<Player>>,
    camera_transform_query: Query<&GlobalTransform, With<Camera>>,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
) {
    trace!("Event Handler: shoot_grenade");
    let player_entity = player_entity_query.get_single().unwrap();
    for _event in shoot_events.read() {
        for global_transform in camera_transform_query.iter() {
            let camera_position = global_transform.translation();
            println!("Camera Position: {}", camera_position);
            let grenade_position = camera_position + (global_transform.forward().as_vec3() * Vec3::new(50., 50., 50.));
            println!("Grenade Position: {}", grenade_position);
            let direction = global_transform.forward();
            let linear_velocity = direction.as_vec3() * Vec3::new( 2., 2., 2. );
            let grenade_id = commands.spawn((
                    Grenade,
                    RigidBody::Dynamic,
                    Collider::capsule(0.01, 0.01),
                    Mesh3d(meshes.add(Capsule3d::new(0.01, 0.01))),
                    MeshMaterial3d(materials.add(Color::WHITE)),
                    Transform::from_scale(grenade_position),
                    LinearVelocity(linear_velocity),
                    Restitution::new(0.),
            )).id();
            commands.entity(player_entity).add_child(grenade_id);
        }
    }
}
#

And here's the error I'm getting:

thread 'main' panicked at /home/tamer/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/avian3d-0.2.1/src/collision/collider/mod.rs:378:9:
assertion failed: b.min.cmple(b.max).all()
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Encountered a panic in system `avian3d::collision::collider::backend::update_aabb<avian3d::collision::collider::parry::Collider>`!
Encountered a panic in system `avian3d::schedule::run_physics_schedule`!
Encountered a panic in system `bevy_app::main_schedule::FixedMain::run_fixed_main`!
Encountered a panic in system `bevy_time::fixed::run_fixed_main_schedule`!
Encountered a panic in system `bevy_app::main_schedule::Main::run_main`!
#

I think I have my translation math off

sweet sundial
#

do your printlns fire?

somber portal
#

yes

#

Here's the results from the same run that produced the errors:

#
Camera Position: [6.728061, -1.9237963, 27.680433]
Grenade Position: [37.01163, -9.692204, -11.339529]
Camera Position: [0, 0, 15]
Grenade Position: [0, 0, -35]
sweet sundial
#

you should really cache the mesh & material btw

somber portal
#

Noted.

#

I do normally. I'm just trying to figure out this error real quick.

sweet sundial
#

take out the as_vec3 and pseudo-splat, there's an impl for Dir3 * f32

somber portal
#

thanks

sweet sundial
#

not sure if vec3*vec3 actually does something weird

somber portal
#

I can println the velocity real quick

sweet sundial
#

why add the grenades as children of the player?

somber portal
#

I think I was originally using that to influence position, but then I thought later I might use it to associate who shot what.

#

Might be a better method though, like an Entity id in the Grenade component

sweet sundial
#

if you're on 0.16 there're custom relations

somber portal
#

Nope. I'm waiting for some stuff to update

#

I can always redesign it later.

#

I'm just trying to do a quick and messy solution to test things out.

sweet sundial
#

does it work without the hierarchy

somber portal
#

let me see

#

Yes and no

#

Yes, it works

#

No, the position is not based on player position

#

Also, not the shape I was expecting, but that's ok for now

sweet sundial
#

... why are you doing Transform::from_scale(position)?

somber portal
#

You know what. I don't remember why

sweet sundial
#

gonna poke your enemies with the other end of the grenade

somber portal
#

I think I got it

vestal minnow
#

Having better momentum conservation would be great, as long as it doesn't hurt perf in any meaningful way

somber portal
#

Ok, now it seems like I'm spawning two grenades at the same time.

#

one based on player

#

one not

sweet sundial
#

ww

#

you're iterating on all your queries, of course one of them's going to happen twice

somber portal
#

fair

#

Still not sure why one seems to be spawning from nowhere though

sweet sundial
#

ui camera if i had to guess

somber portal
#

I'll have to use the With<PlayerCamera> tag

vestal minnow
#

Added back the optimization to ignore gyroscopic torque for bodies with isotropic inertia tensors. This removes the overhead pretty much entirely in my cube-heavy test scene

#

I'll clean it up more tomorrow along with making a PR with a bunch of other reworked logic for velocity integration (pre-computed velocity increments and damping operands, reworked force API, etc.)

sweet sundial
#

excited to add gyroscopic torque to my game about rotating at upwards of 11000rpm

little maple
#

there might be a regression on LockedAxis on main.. my axis doesn't seem to be getting locked anymore

#

LockedAxis works for me on 0.3.0 but it doesn't work on the solver-bodies branch

maiden charm
#

Can I use compound to translate a single shape? The lack of the ability to translate a shape away from the origin is a major pain point in Rapier and I hope Avian can do better?

sweet sundial
#

i don't see how it would break, but doesn't rapier also use parry?

vestal minnow
vestal minnow
#

You can also make the collider a child entity and translate that

#

Parry doesn't really have a convenient way to describe affine transformations for shapes, beyond storing them somewhere externally and transforming the isometries passed to the methods that do the various geometric queries, so it'd be a bit awkward to support it currently

#

In my own collision lib that I'm developing, I want to try and support full affine transformations and have wrappers like Transformed<T>, that way the shape itself stores its transformation if it needs it

vestal minnow
#

I'm using the main branch (since the solver body stuff was merged already)

little maple
lyric sorrel
# lyric sorrel Neat, that looks fairly similar to the approach I took. I noticed the old algori...

Got it running, results look pretty qualitatively similar between our two approaches (I modified the gyroscopic motion example to compare the two on my branch). Code comparison here https://github.com/Jondolf/avian/compare/main...unpairedbracket:avian:am_conservation_new - it definitely wouldn't be mergeable in its current state (haven't tested everything would work fine with joints, pretty sure avian2d won't even compile, etc.) and they both give similar enough results that it probably wouldn't be worth the change anyway (at a guess I'd expect yours to be quicker), but if you'd like to look at it in PR form I'd be happy to open one of course

#

Comparison: red is the Augmented 2nd Order method from the Buss paper, white is what's on avian main rn

sweet sundial
#

does the paper have measurements of a real-world example in it? which of those gives the more accurate period?

vestal minnow
#

some of that could probably be refactored to be a bit more optimized though

vestal minnow
#

Hmmm ๐Ÿค” My prototype for the reworked force API had a ForceHelper system parameter, and using it looked like this:

fn apply_forces(query: Query<Entity, With<Foo>>, forces: ForceHelper) {
    for entity in &mut query {
        forces.entity(entity)
              .add_force(force)
              .add_torque(torque)
              .add_linear_impulse(impulse)
              .add_angular_acceleration(acceleration);
    }
}

However, it's not ideal in terms of perf since it needs to query for the relevant components with get_mut each time instead of having direct access while iterating. And having to give an entity each time is a bit annoying.

I realized we could also use a QueryData type like RigidBodyForces, which would be much more optimal and also provide a nicer API:

fn apply_forces(query: Query<RigidBodyForces, With<Foo>>) {
    for forces in &mut query {
        forces.add_force(force)
              .add_torque(torque)
              .add_linear_impulse(impulse)
              .add_angular_acceleration(acceleration);
    }
}
#

Neat! I think I had this idea before too, but I didn't like it because it would prevent people from also accessing LinearVelocity and AngularVelocity since the query data needs mutable access to them for applying impulses. We could just expose them as properties that people can access through RigidBodyForces, but it still felt a little jank to me.

However I just realized that since we now have SolverBody, which also stores velocity, we can have RigidBodyForces modify that instead, avoiding any conflicts with people accessing the user-facing velocity components :D

#

Now I just need to rework my existing code to this approach

#

I was thinking, do we really even need ConstantForce and ConstantTorque components if we have this RigidBodyForces API? What are some practical use cases where you need persistent forces and don't want to accumulate forces that get automatically cleared

#

I think Godot and Rapier are really the only projects where I've seen persistent forces

#

Unity, Box2D, Jolt, PhysX don't

#

We can easily implement them, but in that case I would make them only support world-space forces applied at the center of mass, otherwise the API gets kinda hairy again

little maple
#

like... a constant forward force

vestal minnow
#

I feel like a more practical thing to have a persistent API for is acceleration, i.e. ConstantLinearAcceleration and ConstantAngularAcceleration components (though the names are kinda ugly...)

#

That way you could have different per-body gravity

#

But persistent forces feel like a kinda weird thing to support imo, it's also easy to implement with your own component where you just store a force and then apply it once at the end with the RigidBodyForces API

little maple
vestal minnow
#

damn this force rework is turning into such a big overhaul again to handle all the various use cases in a nice and efficient way

#

handling local forces, torques, and acceleration properly with substepping in addition to supporting world-space variants is a pain

#

I think I'm landing on a pretty nice design tho

sleek thicket
vestal minnow
# sleek thicket is there anywhere i can see the use cases?

I mean just supporting pretty much every way that you might want to apply forces. The same ones as in e.g. Unity: world-space forces at the center of mass or at a specific point, local forces at the center of mass, and torque in both world space and local space. And it should support all the different "force modes" (force / impulse / acceleration / velocity change)

Currently, I have all of these methods, though a few are still missing

  • apply_linear_impulse
  • apply_linear_impulse_at_point
  • apply_local_linear_impulse
  • apply_angular_impulse
  • apply_local_angular_impulse
  • apply_force
  • apply_force_at_point
  • apply_local_force
  • apply_torque
  • apply_local_torque
  • apply_linear_acceleration
  • apply_local_linear_acceleration
  • apply_angular_acceleration
  • apply_local_angular_acceleration
    (we could also have something like Unity's ForceMode if we want to make the API surface smaller, but I don't like calling impulses and acceleration "forces", and the actual code with that is slightly more verbose because of the extra enum you need to pass)

Impulses just modify velocity directly, so those are straightforward. World-space forces and acceleration both just turn into velocity increments (if you ignore changes in the inertia tensor across substeps), but ideally you only multiply the forces by the mass properties once when many forces are applied, so you still need to store them separately. And then local forces and acceleration also need to be handled and stored separately, because they need to be rotated with the body at each substep to work correctly, unlike the world-space versions

sleek thicket
#

looks like adding bool for local would remove half of them

#

is the signature different between local and global?

vestal minnow
# vestal minnow I mean just supporting pretty much every way that you might want to apply forces...

So you end up needing to store at least

  1. World-space increments for linear and angular velocity
  2. World-space forces and torques
  3. Local forces and torques
  4. Local linear and angular acceleration (or local velocity increments)
    But for most games, you're only applying forces occasionally and for individual bodies, in which case you only need (1), and don't want the overhead of 2-4. My current design does this by automatically adding/removing the other components when they have zero values for an entire physics step, indicating that it's probably not a continuously applied force/acceleration
vestal minnow
#

Unity is the same

#

either way I don't like a bool for this, that just makes the code harder to read with the only real benefit being that we have less methods in the API

#

plus it would add branching to all the methods, unless Rust compiles that out

sleek thicket
vestal minnow
#

I assume it'd do it at least if you use const generics but idk what it does if you just pass in a bool as an argument

vestal minnow
#

I would need to make a custom QueryWithForces system param for that ๐Ÿ™ƒ which wouldn't be great

#

As far as I can tell, the options are

  1. Use a QueryData type and eat up the cost of storing both forces and accelerations in both local and world space for every dynamic body
  2. Use a custom SystemParam that handles the automatic insertion of the necessary components if they don't already exist
#

the API is nicer for option 1 but I really don't like the unnecessary overhead

#

I need QueryData to allow access to Commands or something for adding components :P

sweet sundial
#

is there overhead to making it a required component?

vestal minnow
#

The point is that I don't want every dynamic body to store like six extra vectors just because they might have forces applied to them sometimes. That would unnecessarily waste memory and also make applying the actual forces slower since it then needs to iterate over forces that do nothing

vestal minnow
#

Okay well it seems like our best bet is my earlier approach of a separate system param. Arguably, I think this makes more semantic sense anyway. A force or impulse isn't really a property of the body, it's rather an external interaction, so when you're applying forces, it makes sense that the API is like "for entity, apply a force" rather than querying for forces associated with bodies

#

And a separate system param is useful in other ways: we can use it to apply explosion forces in the world (Box2D has this) or even have helpers for attractors and such

#

So it'd be like

fn apply_forces(query: Query<Entity, With<Foo>>, forces: RigidBodyForces) {
    for entity in &mut query {
        // Apply force to `entity`.
        forces
            .entity(entity)
            .add_force(force)
            .add_torque(torque);

        // Apply a radial explosion in the world (Box2D has this).
        // TODO: Implement this lol
        forces.apply_explosion(explosion);
    }
}
#

And I think in addition to this we should probably have at least the ConstantForce and ConstantTorque components too, Unity Physics (the DOTS version) has them too

#

god this force rework is nearly as bikesheddy and tricky in all the subtle ways as the mass property rework

#

Oh I think this could also act as an API for retrieving stuff like joint forces ๐Ÿ‘€ they might be not as conveniently exposed after the joint rework and some solver optimizations, we'll see

vestal minnow
#

omg it works, this is the most over-engineered force system... these are just the types needed to make the ForceHelper API function lol

#[derive(QueryData)]
#[query_data(mutable)]
struct RigidBodyForceQuery {
    position: Read<Position>,
    rotation: Read<Rotation>,
    linear_velocity: Write<LinearVelocity>,
    angular_velocity: Write<AngularVelocity>,
    mass_props: Read<SolverBodyInertia>,
    center_of_mass: Read<ComputedCenterOfMass>,
    locked_axes: Option<Read<LockedAxes>>,
    integration: Write<VelocityIntegrationData>,
    accumulated_world_forces: Option<Write<AccumulatedWorldForces>>,
    accumulated_local_forces: Option<Write<AccumulatedLocalForces>>,
    accumulated_local_acceleration: Option<Write<AccumulatedLocalAcceleration>>,
}

#[derive(SystemParam)]
pub struct ForceHelper<'w, 's> {
    query: Query<'w, 's, RigidBodyForceQuery>,
    commands: Commands<'w, 's>,
    insert_buffers: Local<'s, ForceInsertionBuffers>,
}

pub struct EntityForces<'w> {
    entity: Entity,
    body: RigidBodyForceQueryItem<'w>,
    insert_buffers: &'w mut ForceInsertionBuffers,
}

#[derive(Default)]
struct ForceInsertionBuffers {
    world_forces: EntityHashMap<AccumulatedWorldForces>,
    local_forces: EntityHashMap<AccumulatedLocalForces>,
    local_acceleration: EntityHashMap<AccumulatedLocalAcceleration>,
}
#

for bodies that don't have the force components, I have to accumulate forces into separate buffers, and then when drop is called for the ForceHelper system param, I drain the buffers and batch-insert the corresponding components

sweet sundial
#

neat

dusty bolt
#

Hey Jondof, this is probably not a question you get very often, but have you every considered taking the Position and Rotation components you use in Avian and maybe making them their own crate? I've been using them when i want to set transforms on a global level. Plus, it means not without worrying about all the transform_point business you get from trying go global to local or vise versa or whatever else with bevy's transform components which has been a major pain point in various projects of mine. i even might add avian to an old IK project of mine that i might start working on again, just for those two components. although, i still have to work out how position fits in with the TransformsHelper system param which i need in some specific cases.

sweet sundial
#

doesn't globaltransform have a bunch of helpers?

dusty bolt
# sweet sundial doesn't globaltransform have a bunch of helpers?

i'm referring to what i now realize is a system param (not a plugin, which is what i called it), which recalculates the targeted global transform on the spot with a function. I found it necessary when updating a bunch transforms at the same time, that are all in a hierarchy with each other, like when you create a bone rig in blender or wherever else and each bone is child of the last.

#

I just did some reading on the sync plugin on the avian docs, and it seems that i don't need to use transfom helper, which is thankful. although it runs in fixed which i now realise has probably been messing me up

sleek thicket
dreamy viper
#

I have an example where i push a ball against a wall; more often than not the ball starts gliding against the wall instead of cleaning bouncing off of it. Is there a component i need to add to prevent this?

sweet sundial
#

what's your coefficient of restitution

dreamy viper
#

I had ColliderDensity(0.05), on my ball

#

and ColliderDensity(0.1) on the wall

sleek thicket
vestal minnow
#

I'd maybe rename them like GlobalTranslation2d/GlobalTranslation3d and GlobalRotation2d/GlobalRotation3d

#

which then write to the GlobalTransform if present

dull mantle
#

Hi guys, maybe a dumb question but is there a specific avian system set or schedule I should run a y_sort system? When I simply run it on Update or in a set that runs after my movement systems, it affects the current speed of movement (being very slow).

visual sparrow
dull mantle
#

I guess I just meant a system that mutates my Transform z based on y

vestal minnow
#

hhh I think I need to rework how we initialize Position and Rotation and finally make them required components instead of inserting them manually... somehow this also relates to the force rework, don't ask why lol

#

it's just a painnn to handle the hierarchy and scene loading and everything properly if they're required components

vestal minnow
dull mantle
#

Yes

vestal minnow
#

yeah it has the known problem that mutably accessing Transform at all outside the fixed time step, e.g. in Update, breaks interpolation

#

for now you could try either disabling interpolation (and doing it manually if you want) or alternatively doing y-sorting in a schedule like FixedUpdate

dull mantle
#

Ok, great thanks!

vestal minnow
plucky elbow
#

so i see RigidBodyQuery
is there a way to tell it to exclude static bodies?

vestal minnow
#

in 0.3 there's no component that you can use to filter rigid body types, but you can just check !rb.is_static()

#

RigidBodyQuery isn't necessarily meant for users though, it's intended more for internals (though I use it less nowadays)

plucky elbow
#

works fine without mut tho

vestal minnow
#

with QueryData types you don't use &mut at all, just use Query<RigidBodyQuery>

plucky elbow
#

then how do i get mutable access?

vestal minnow
#

it gives you mutable access to the components that are specified as &'static mut in the RigidBodyQuery

#

unless you're using RigidBodyQueryReadOnly

#

also FYI I might remove RigidBodyQuery at some point, or at least change it a lot, since it's slow and ugly and I don't need it much in its current state anymore

vestal minnow
#

Just query for the components like you normally would, or make your own QueryData type if you want a similar convenience API that works better for your use case

#

If you're doing custom gravity or attractor stuff then I imagine you only need GlobalTransform and ExternalForce and/or LinearVelocity

#

or something similar

plucky elbow
#

maybe cause i used apply_force?

#

tbf with the game im making i just need gravity to be in the direction away from 0,0,0

plucky elbow
#

this feels wrong... but the impl for gravity internally didnt help me much

#

i do have this weird shaking? im guessing cause normal gravity takes into account if in contact with something?

plucky elbow
#

Maybe I should just fork and impl my change where gravity is normally calculate?

plucky elbow
#

need to rotate with gravity and make directions local space based prob...

plucky elbow
#

wtf is rhs?

plain lion
#

right-hand side

sweet sundial
#

in the parameter list at the top

plucky elbow
plain lion
#

nothing to do with coordinates/axes. lhs and rhs are used to mean the left and right sides of a binary expression. "rhs" is the target rotation

plucky elbow
#

hmm
idk what i need to do
just trying to make local up point to 0,0,0

#
    for mut transform in player.iter_mut() {
        let current_up = -transform.down();
        let target_up = -transform.translation.normalize();
        let rotation = current_up.angle_between(target_up);
        if rotation > 0.01 {
            let axis = current_up.cross(target_up);
            transform.rotate(Quaternion::from_axis_angle(axis, rotation));
        }
    }
}```
yay
sleek thicket
oak vigil
#

Hey, since the 0.3.0 update, I'm having an issue where my child colliders no longer seem to update their position when the parent rigid body has moved:

oak vigil
plucky elbow
vestal minnow
#

Today's joint rework progress

  • Implemented a fixed joint (aka weld joint) for 2D and 3D, just with a separate point-to-point constraint and a fixed angle constraint
  • Implemented a prismatic joint (aka slider joint) for 2D, with a block solver like in Box2D
  • Looked more into how Bepu does things, researched a bunch of random stuff
  • Thought a bit more about how we should best store and manage joints, and what the API should be like
#

Tomorrow I'll probably try to

  • Implement a prismatic joint for 3D
  • Reimplement the fixed joint with a block solver... for 3D this requires an LDL^T decomposition of a 6x6 matrix among other things
  • Implement a twist constraint for the spherical joint
  • Debug why the block solver for the 3D hinge joint is broken
vestal minnow
plucky elbow
# plucky elbow Didn't have that Only issue atm is damping being in world space instead of local...
let player_transform = player.single().unwrap();
        let rotation = player_transform.rotation;
        let new_velocity = Vec3::new(linear_velocity.x, linear_velocity.y, linear_velocity.z);

        let final_velocity = LinearVelocity(rotation.mul_vec3(new_velocity));
        linear_velocity.x = final_velocity.x;
        linear_velocity.y = final_velocity.y;
        linear_velocity.z = final_velocity.z;

        linear_velocity.x *= damping_factor.0;
        linear_velocity.z *= damping_factor.0;```
jesus im bad at math
vestal minnow
# plucky elbow ```rust let player_transform = player.single().unwrap(); let rotation = ...

a scuffed way that I would handle damping for the player's local XZ plane is like this (untested)

fn movement_damping(mut bodies: Query<(&mut LinearVelocity, &GlobalTransform)>, time: Res<Time>) {
    let delta_secs = time.delta_secs();
    let damping_coefficient = 0.2;

    for (mut lin_vel, global_transform) in &mut bodies {
        let rotation = global_transform.rotation();

        // Transform the linear velocity to local space.
        let mut local_lin_vel = rotation.inverse() * lin_vel.0;

        // Apply damping (Padรฉ approximation of exponential decay).
        let damping_factor = 1.0 / (1.0 + delta_secs * damping_coefficient);
        local_lin_vel.x *= damping_factor;
        local_lin_vel.z *= damping_factor;

        // Transform the linear velocity back to world space.
        lin_vel.0 = rotation * local_lin_vel;
    }
}

edit: just moved the damping factor to its own variable to reuse it for x and z

plucky elbow
#

hmm

#

ill give it a shot

plucky elbow
strong rampart
#

Hi guys, I have a question about the transfer of force from static or kinematic objects moved by manual transform to dynamic objects. I want to shoot a dynamic ball into the playing field as if pushed by a spring. However, the force does not seem to be transferred by manually setting the transform object. What is the best way to get the spring to push the ball? Or do you have to manually add a velocity component to the ball in the right direction? Thanks for the answer ๐Ÿ™‚

visual sparrow
#

^ this is for running tests that require RenderPlugin. @vestal minnow we could enable the collider constructor hierarchy test on Linux again if we set this up

#

Not a priority imo, but good to know ๐Ÿ™‚

edgy light
#

or run them on a mac runner or on a windows runner, they have builtin support for "gpu"

#

and as a bonus the macos runners are the fastest in github actions

visual sparrow
#

In order to make sure that determinism across OS is working

knotty thicket
edgy light
visual sparrow
vague pebble
#

Question, lets say I have a collider, yet I dont know what it is length might be all I know it is a capsule. How can I get that information in another system?

visual sparrow
vague pebble
#

But I thank you for your answer

willow elbow
#

Hello, I saw that we can now use observers for collisions instead of reading events. However, with events I can set the system to run in the State that I want, is there a way to do so with observer systems too?

royal helm
#

is you compound a bunch of colliders together, does it prevent ghost collisions?

plucky elbow
oak vigil
#

I'm curious if anyone else has ran into this

sleek thicket
vestal minnow
#

We could make this less of an issue by requiring it through a second layer though, not directly through Collider. Every collider requires a ColliderMarkercomponent, so if we made that require CollisionLayers instead, it'd have a slightly lower specifity and wouldn't conflict with user logic as often

#

Kind of hacky but might work :P

vestal minnow
# royal helm is you compound a bunch of colliders together, does it prevent ghost collisions?

Jolt supports this through enhanced internal edge removal and I have an issue for it
https://github.com/Jondolf/avian/issues/612

GitHub

Ghost collisions are a very common and frustrating problem in game physics. A common case where they may occur is when an object is sliding along a seemingly smooth surface formed by several shapes...

#

but we don't have it yet (and neither does Rapier)

#

it's kinda annoying that it doesn't work for T-edges though, and requires the shapes to belong to the same body

#

I think theoretically it might be possible to fix both of these but it'd be more expensive and complicated

oak vigil
#

If a component requires just for ABC to exist, it should never override a requirement of ABC::XYZ

vestal minnow
#

I now have (mostly) functional graph coloring for the solver. Still needs work, but early results are looking promising ๐Ÿ‘€
Before and after:

#

This is solving constraints in parallel chunks, and will allow us to use wide SIMD later which should further improve solver performance even when single-threaded

#

Unlike Rapier which (to my knowledge) only has multi-threading between islands, graph coloring also works for parallelizing within islands

#

In this test scene all the bodies are part of the same pyramid stacked on top of each other

#

I should make some proper benchmark to see how well both Avian and Rapier scale with multi-threading ๐Ÿค”

sweet sundial
#

is that a 25 tall pyramid (out of curiosity)

vestal minnow
#

the graph coloring looks pretty fun at a larger scale like this

vestal minnow
sweet sundial
#

ah, 2d

vestal minnow
#

yeah 3D works too but in 2D it's easier to see and test things

sweet sundial
#

but yeah that looks like very good parallelism

vestal minnow
#

wait I wasn't even running in release mode ๐Ÿ‘€ though I think I have some debug optimizations enabled

sweet sundial
vestal minnow
#

hah well here we go, further down from 10 ms to 7.1 ms

#

on the main branch with release mode it's 11.5 ms

#

also I'm using Bevy's ComputeTaskPool here which limits the available threads, so it could be even more parallel technically

#

and then when I do wide SIMD it should be another nearly 2x perf improvement, at least judging by Box2D v3's numbers

#

I think a decent perf target for the solver right now would be like 10k bodies in a single stack at 60 fps, of course if they're not in the same stack like that then you could have way more

#

OH well nevermind, apparently we can already handle that with this graph coloring

#

geez

#

...I guess over 20k is the new target? ๐Ÿ˜„

#

honestly if you have this many bodies stacked like this in a game... what are you even doing lmao

cinder summit
#

Crysis barrel stresstest

vestal minnow
cinder summit
#

Certainly a better use of electricity than running AI

vestal minnow
#

also it's pretty insane how stable the "new" impulse-based solver is... this is just 6 substeps and the whole pyramid stays perfectly stable

#

imagine this with the old XPBD solver ๐Ÿ˜‚

cinder summit
#

Wouldn't even have performed at all thonk

visual sparrow
vestal minnow
visual sparrow
#

I guess that's a good plan if we also needed to support a theme like "blast from the past"

vestal minnow
#

at least Bevy 0.10 added "stageless" scheduling aka schedule v3

#

working with pre-stageless would be fun

cinder summit
gentle steppe
#

I've been gone so long that xpbd is renamed. avian is more memorable. so good change.

cinder summit
spiral nymph
hard helm
fleet field
#

hey quick question, is there an easy way to disable gravity for a specific entity? I have an interpolated entity using the lightyear crate, which is behaving strangely when it jumps, I think because the client is applying gravity to it a second time, after the server already performed that calculation

#

ie. I don't want to have physics run for these interpolated entities

sweet sundial
#

though it seems like you'd want parallel identical simulations

fleet field
#

thanks. I found RigidBodyDisabled which seems to be what I need. I would want parallel identical simulations if the entity is predicted, but not if it's interpolated, as far as I understand

modern cypress
#

Hi I have a question about ColliderDisabled. It seems to work inconsistently sometimes it behaves as expected, but other times it doesnโ€™t. In the video I attached, you can see that when I remove ColliderDisabled, the object occasionally falls through the ground. Iโ€™ve included the relevant code below.
Itโ€™s possible that Iโ€™m using it incorrectly, so Iโ€™d really appreciate any suggestions. Thanks in advance!
https://gist.github.com/maple658/77dd2d0d8c7781b8a8923e10a47b2050

vestal minnow
#

It's strange that it's inconsistent like that ๐Ÿค” IIRC the logic for ColliderDisabled mostly relies on hooks and observers, so there shouldn't really be scheduling inconsistensies there

#

Hmm... I wonder if Bevy still has the problem of RemovedComponents<T> not working well in FixedUpdate, if so then that's probably it

cinder summit
#

RemovedComponents<T> gets cleared at the end of each frame iirc

vestal minnow
#

We currently use that in one place for adding colliders back to the broad phase when they're re-enabled

#

right

#

we probably just need to switch that to use observers

#

The docs for RemovedComponents<T> say

This acts effectively the same as an EventReader.
but if it misses events in FixedUpdate then that's not true :P

cinder summit
#

I mean you can clear EventReaders manually and break that behavior

#

Or I guess rather clear the Events

vestal minnow
#

yeah I guess

#

also I think I need to partially redo my graph coloring, it's currently UB if a contact pair has more than one manifold ferris_spooky and also won't work well for wide SIMD

#

For wide SIMD we need each constraint to have a known number of contact points (typically up to 4) to vectorize it properly, but since a manifold could have more than that, we'll need to create potentially multiple constraints per manifold... and there can also be more than one manifold

#

each body can only appear once in a given color so the extra constraints need to be put in different colors

#

so we need to somehow do the coloring at the constraint-level instead of at pair-level like Box2D

vestal minnow
#

mm I should probably solve the "more than 4 points" problem by doing manifold reduction

#

so

  1. Perform manifold reduction to get a maximum of 4 points per manifold
  2. Create a constraint for each manifold, doing graph coloring at the constraint level
    especially for wide SIMD, we'd ideally also have separate constraint types for manifolds with 1, 2, 3, and 4 points, like Bepu, but we can probably just start by always storing 4 in 3D or 2 in 2D for wide contacts ๐Ÿค”
#

and handle overflow contacts normally like we currently do

little maple
#

I'm curious what graph coloring is

surreal rune
# little maple I'm curious what graph coloring is

Generally, it's how you can categorise "things" that have relationships. There's some interesting laws you can derive from graph colouring, like that 4 colours is sufficient to categorise any graph map while ensuring no two connected nodes share the same colour

surreal rune
vestal minnow
# little maple I'm curious what graph coloring is

The best resources I have for this in the context of simulating contacts is the Box2D article on SIMD and the Intel paper on multi-core simulation
https://box2d.org/posts/2024/08/simd-matters/
https://web.eecs.umich.edu/~msmelyan/papers/physsim_onmanycore_itj.pdf

Basically, contacts are solved with contact constraints, and they naturally form a graph where the rigid bodies are nodes and the constraints are edges. With a Gauss-Seidel solver, these typically need to be solved sequentially, because one body could participate in multiple constraints that all affect each other, and you could get race conditions when accessing the bodies in parallel.

Graph coloring solves this by assigning each constraint to a "color" such that no two adjacent edges share the same color. This means that each body only appears at most once in a given color, and it's perfectly safe to solve all constraints in a color in parallel and/or in batches with wide SIMD.

There's a lot of theory around graph coloring (that I have not studied :P) but for the purposes of physics it's really quite straightforward, and you can get away with a simple greedy algorithm.

I'm mostly using Box2D's approach: you store a Vec<GraphColor>, where each color contains its constraints and a bitset where set bits correspond to indices of bodies that have been assigned to that color. When a contact is added, you iterate over the colors until you find a free one (i.e. the bodies have not been assigned to that color yet), and then set the corresponding body bits and add the contact there. When a contact ends, you unset the body bits and remove the contact. Easy :D (there is quite a bit of managing indices and also stuff like non-touching contacts and sleeping contacts that complicate things a bit though)

This doesn't necessarily find a perfect graph coloring, but it really doesn't matter much in practice. What matters here is mostly that adding and removing constraints is fast and the graph coloring can be persisted across frames rather than running some expensive algorithm every frame to find the optimal coloring.

wanton scaffold
#

Hi everyone, I've been using a lot of convex decomposition, but the calculation is quite slow. Is there a way to save the results of the convex decomposition?

visual sparrow
#

It would be nice to have some asset preprocessing to do this before even launching the game, but thatโ€™s not builtin yet

wanton scaffold
#

Thank you.

formal gorge
#

I'm getting really bad perfomance with a bunch of static bodies with colliders. Is there anything I can easily do to improve this?

#

e.g. is there something configurable in the broad phase or something? changing the colliders to cuboids improves the performance (even though no dynamic bodies get anywhere near them)

little maple
formal gorge
#

on further testing, this occurs even if there are no non-static bodies anywhere

#

I would have presumed that static bodies should only have a performance impact if there is at least one dynamic or kinematic body?

#

also, ensuring that none of the static bodies' colliders intersect with each other does improve performance significantly (though it's still not good)

#

so I guess that at least answers the direct cause, even if it still feels surprising to me that it is happening

#

so is there, perhaps, a way to disable collision detection for static bodies with each other?

vestal minnow
#

The plan is to switch to a broad phase that uses separate BVHs for dynamic and static/sleeping bodies, which would fix this since it'd only run intersection tests for moving bodies and have basically zero cost for the static case

#

I'm hoping to have that implemented for the next release ๐Ÿคž

formal gorge
#

I guess it's pretty immaterial then, but out of curiosity does it early out before the AABB test? because changing the collider position (I just gave them a big vertical offset) impacts performance rn

#

actually, rereading what you said, it earlies our between broad and narrow IIUC

#

if so, that makes sense

vestal minnow
#

The order of the early-outs is currently this

// x doesn't intersect; check this first so we can discard as soon as possible.
if aabb2.min.x > aabb1.max.x {
    break;
}

// y doesn't intersect.
if aabb1.min.y > aabb2.max.y || aabb1.max.y < aabb2.min.y {
    continue;
}

#[cfg(feature = "3d")]
// z doesn't intersect.
if aabb1.min.z > aabb2.max.z || aabb1.max.z < aabb2.min.z {
    continue;
}

// No collisions between bodies that haven't moved or colliders with incompatible layers
// or colliders attached to the same rigid body.
if flags1
    .intersection(*flags2)
    .contains(AabbIntervalFlags::IS_INACTIVE)
    || !layers1.interacts_with(*layers2)
    || collider_of1 == collider_of2
{
    continue;
}

IS_INACTIVE indicates that it's a static-static or static-sleeping case and should be ignored. So it's after the AABB intersection test

#

The X axis is tested first since the current sweep and prune algo sorts the AABBs on the X axis so that's the main termination criterion

#

the current impl is very basic and old, there are more sophisticated ways to do sweep and prune too

#

but BVHs are more commonly used (Box2D, Jolt, Bepu...) and also needed for spatial queries so it'd be nice to reuse them for both the broad phase and spatial queries

formal gorge
#

That's super helpful, thanks!

quartz heart
#

With picking, the target is the child entity which has the collider.

Is there an easy way to get the associated Rigidbody of the collider without traversing the hierachy?

quartz heart
#

Ah just found: ColliderOf that solves it ๐Ÿ˜…

sick oracle
#

Having trouble getting colliding entities.
I tried via observers (never triggers), EventReader (always empty) and "normal" queries (always empty). Even though the physics work as expected with RigidBody::Dynamic, all collision events come out empty. This happens with or without setting any non-default collision layers.

I spawn ColliderConstructurHierarchy, CollidingEntities, and CollisionEventsEnabled and then with_children() a SceneRoot of interest.

Is there anything special I should watch out for that would prevent collisions from being in the query set? Performance goes down as more objects collide which suggests to me that the collisions really are happening at least?

visual sparrow
#

@vestal minnow would you accept a PR that added collider caching for just Handle<Mesh> to Collider when using ColliderConstructor and ColliderConstructorHierarchy?

#

Ideally, we would have a way to cache any and all colliders, but I think caching the ones made from a mesh with these methods would already bring us 90% of the way, from a user perspective

#

I'm asking because I've just discovered that convex decomp takes half of my frame time when loading my map ๐Ÿ‘€

vestal minnow
visual sparrow
#

Use that one internally while running init_collider_constructors

#

expose a ColliderCachePlugin in case the user want to disable it for some reason

#

Hide it all behind #[cfg(feature = "collider-from-mesh")]

#

I tried doing this in a third-party crate first, but I would end up duplicating the code of init_collider_constructors

#

So I'd prefer to upstream it

vestal minnow
#

Yeah that sounds good

visual sparrow
#

Much better ๐Ÿ˜„

fallow pike
#

Helo everyone

vestal minnow
# sick oracle Having trouble getting colliding entities. I tried via observers (never triggers...

Hmm this is kind of an interesting case. So when you're spawning a scene in Bevy, it typically creates a hierarchy with an entity for each object in that scene, and the purpose of ColliderConstructorHierarchy is to automatically generate colliders for those entities. You've added CollidingEntities and ColliionEventsEnabled for that scene root, but the actual colliders deeper in the hierarchy don't have them, which is why you're not getting collision events for them. ColliderConstructorHierarchy has a few helper methods to specify stuff like collision layers and mass properties for the colliders it generates, but nothing for events currently

fallow pike
#

Can help someone? Event CollisionStarted not working

#

Logs:
0
0
0
0
0
0
0
0
0

fn handle_collisions(
    mut commands: Commands,
    mut collision_events: EventReader<CollisionStarted>,
    projectiles: Query<&Effect, With<Projectile>>,
    mut enemies: Query<&mut Health, With<Enemy>>,
) {
    println!("{:?}", collision_events.len());
    for CollisionStarted(entity1, entity2) in collision_events.read() {
        if let Ok(effect) = projectiles.get(*entity1) {
            if let Ok(mut health) = enemies.get_mut(*entity2) {
                println!("helo2");
                health.0 -= effect.0;
                commands.entity(*entity1).despawn();
            }
        } else if let Ok(projectile) = projectiles.get(*entity2) {
            if let Ok(mut health) = enemies.get_mut(*entity1) {
                println!("helo3");
                health.0 -= projectile.0;
                commands.entity(*entity2).despawn();
            }
        }
    }
}


#[derive(Component)]
#[require(
    RigidBody::Kinematic,
    Collider::circle(5.0),
    Lifetime(Timer::from_seconds(2.0, TimerMode::Once)),
    Effect(-5.0)
)]
pub struct Projectile;


#[derive(Component)]
#[require(RigidBody::Kinematic, Collider::circle(TILE_SIZE / 2.0), Health(20.0))]
pub struct Enemy(pub usize);


fn spawn_enemies(
    mut commands: Commands,
    time: Res<Time>,
    mut spawn_timer: ResMut<EnemySpawnTimer>,
    path: Res<EnemyPath>,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<ColorMaterial>>,
) {
    if path.0.len() < 3 {
        return;
    }

    spawn_timer.0.tick(time.delta());
    if spawn_timer.0.just_finished() {
        println!("spawn {:?}", path.0);
        commands.spawn((
            Mesh2d(meshes.add(Circle::new(TILE_SIZE))),
            MeshMaterial2d(materials.add(Color::from(RED))),
            Transform::from_translation(path.0[0].extend(1.0)),
            Enemy(1),
        ));
    }
}```
vestal minnow
sick oracle
#

@vestal minnow Got it. I guess I can just manually iterate the hierarchy and spawn CollidingEntities + CollisionEventsEnabled manually no any parent of a Mesh or something to that effect then

vestal minnow
#

yeah

vestal minnow
visual sparrow
#

But there's a tiny issue

vestal minnow
#

uhhhh

visual sparrow
#

I don't know whether that is just what avian's main does right now or whether that's my PR hmm

#

I checked the generated colliders and they look the same visually

#

at least, nothing that is obviously wrong

#

Let me check main real quick

visual sparrow
#

Bad news, sorry ๐Ÿ˜ฌ

#

But also, that means my PR is fine, which is good for me ๐Ÿ˜„

#

I can show you some nice speedups from the PR tho, sec

vestal minnow
#

mm that is almost certainly solver bodies since not much else has changed after 0.3 on main

#

no idea why it'd do that tho

visual sparrow
vestal minnow
#

71b835c280d7b909953b36f895f3f258b43df306 is right before solver bodies

visual sparrow
vestal minnow
visual sparrow
little maple
#

I wonder if it's the same thing I saw with locked rotations or at least related?

#

I couldn't figure out what was causing that

vestal minnow
#

yeahhhh I can probably repro this by just running foxtrot on Avian main right?

visual sparrow
#

So yeah ๐Ÿ˜„

#

(with my fork being up-to-date with avian's main, of course)

vestal minnow
#

cool, I'll try to look into this later today then

visual sparrow
#

@vestal minnow ColliderConstructor contains f32, so it's not Eq, so it's not Hash, so it cannot be part of the key

#

I could either

  • Leave the PR as-is
  • Put ColliderConstructor into the value and Invalidate the cache as soon as a new constructor was used for a mesh
  • Make the value a Vec<(ColliderConstructor, Collider)> and iterate through that to find the right collider
  • Wrap ColliderConstructor so that it is Hash and put it in the key
#

Your choice

vestal minnow
#

hmm right that's kinda annoying

visual sparrow
#

I'm personally tending towards option 3

#

Don't expect that vec to ever be big enough to matter

#

But it's a weak tendency

vestal minnow
#

3 is probably fine for now

visual sparrow
#

๐Ÿ‘

vestal minnow
#

We could optimize for size a bit better with a separate ColliderFromMesh enum with only variants like Trimesh, ConvexDecomposition, and ConvexHull, which would make it the size of VhacdParameters

#

but idk probably not worth it for now

visual sparrow
#

Yeah that's out of scope for this PR

vestal minnow
#

the size of ColliderConstructor isn't much bigger, I think it's just two more vecs for ConvexDecompositionWithConfig

#

(which would be empty here)

visual sparrow
#

@vestal minnow done ๐Ÿ™‚

visual sparrow
visual sparrow
little maple
visual sparrow
#

Which I believe is what most are already using for generating colliders from meshes

little maple
#

oh geez, I'm not most, I need to fix that

visual sparrow
#

Oh btw @dusky brook since you were wondering. Avian's full test suite takes the following amount of time on my machine:

  • cargo test: 10.33 secs
  • cargo nextest: 7.16 secs
    where nextest also gives nicer output
visual sparrow
little maple
vague pebble
# visual sparrow

Does this also happen with common colliders like spheres and so on?

past escarp
#

What would be the recommended way to predict and show the trajectory of a projectile? I assume that is out of scope for avian and usually done manually?

vestal minnow
#

@visual sparrow I'm guessing this is also new bavy

#

now how do I start to debug this...

#

oh now the lantern is a beyblade

visual sparrow
#

But ngl it looks really fun

visual sparrow
#

That must look nice with the dynamic light

vestal minnow
visual sparrow
visual sparrow
vestal minnow
#

some kind of physically simulated beyblade game would be cool

#

I'll actually just take the chair model into a minimal test scene in avian and try to debug that in isolation, might be easier that way

visual sparrow
#

The floor collider is a convex hull FYI

#

in case that matters

vestal minnow
#

it's the new gyroscopic torque I think :/

visual sparrow
visual sparrow
vestal minnow
#

we already had gyroscopic torque before, so if we revert to that it'd still work, it's just slower

#

it's probably just some bug with my impl though, since Jolt uses the same approach

visual sparrow
#

(BIG density, I know)

vestal minnow
#

damn

#

heavy chair

visual sparrow
#

it's a hack to make it less likely to push it around, as that felt annoying

#

E.g. when jumping on the chair at an angle, I want it to stay put

lyric sorrel
#

Oh, I noticed something I thought was a bit fishy in the new gyro torque when I looked at it

#

This bit

#

Does just using the local_inverse_inertia directly fix it?

vestal minnow
#

let's see

#

it seems like it does

#

yup

#

Ah so Jolt does this

#

But inertia_space_to_world_space also includes the orientation of the inertial frame

lyric sorrel
#

I think Jolt does something that looks like that because it uses the eigenvalues + rotation representation of the inertia tensor?

vestal minnow
#

Whereas I only included the body's rotation

#

yup

lyric sorrel
vestal minnow
#

that makes sense

visual sparrow
#

You two figured this out reeeal quick ๐Ÿ˜„

#

Glad my absolutely-not-minimal reproducible example was of help lol

lyric sorrel
#

Oh, while I'm here I think I also noticed an issue in the integrator from the solverbody PR: I think the multiplication on this line is backwards (it's right-multiplying the delta_rotation by the substep's rotation where it should be left-multiplying)

GitHub

ECS-driven 2D and 3D physics engine for the Bevy game engine. - Jondolf/avian

vestal minnow
#

right it should be

delta_rotation.0 =
    Quaternion::from_scaled_axis(*angular_velocity * delta_secs) * delta_rotation.0;
lyric sorrel
#

Yeah, I think so

#

Wouldn't expect it to cause obvious observable problems, but I thought the same about that inertia thing too so shows what I know

vestal minnow
#

The practical difference is that right-multiplication applies the rotation in the local frame and left-multiplication in the global frame right?

lyric sorrel
#

Yeah

#

Trying to figure out exactly what it'll do broke my brain slightly

#

Because it's on a delta rotation that is then applied to an existing rotation

#

Effectively the substeps of a given step get applied backwards, I think

vestal minnow
#

Yeah I think so

#

It feels kinda unintuitive that even though it's applied backwards, it still looks pretty reasonable lol

#

quats in a nutshell

lyric sorrel
#

I think as long as all the substep rotations increments are similar it doesn't make much difference? The final application to the body's rotation is the right way round, which is where it'd go really wrong

vestal minnow
#

yeah

#

I also merged the gyroscopic torque fix already

#

so the chair should no longer fly and the lantern isn't a beyblade anymore @visual sparrow ๐Ÿ˜”

little maple
vestal minnow
#

I'll add a nostalgia feature flag that adds bevy_xpbd 0.1 behavior

visual sparrow
little maple
#

nope, I guess something else may have fixed it ๐Ÿค” oh well, I don't see the issue I was having at least

dusky brook
#

but yeah nbd for game jam, who's writing unit tests

visual sparrow
visual sparrow
#

If you have time, I'd like to see the results.

vestal minnow
visual sparrow
vestal minnow
# visual sparrow

Unfortunately it not only kills the cache but also performance ๐Ÿ˜”

#

Without a killed cache, single-threaded executor (left) and multi-threaded executor (right)

#

With a killed cache

#

...why is behavior different in the versions with a killed cache, and why is it different between the single-threaded and multi-threaded version thonk

#

oh there's some rand stuff that probably messes up archetypes and iteration order or something

#

maybe? idk

visual sparrow
weary mortar
#

I am trying to change the default integrator from "Semi-implicit Euler" to "Velocity verlet", but I am struggling to wrap my head around the recent SolverBody-addition. Specifically the scheduling and substepping part: Impulses and forces seem to be calculated once per "step", however integration happens in each substep. Am I correct in thinking that I essentially need to move all the solver-systems into the SubstepSchedule? Since "Velocity verlet" needs to recalculate the forces ๐Ÿค”

visual sparrow
little maple
#

Please don't use this for real

visual sparrow
vestal minnow
# weary mortar I am trying to change the default integrator from "Semi-implicit Euler" to "Velo...

A lot of the integration and force logic will be very different with the upcoming force rework (WIP branch here). But the semantics are the same: impulses are applied directly to the velocity once per step (really it should be applied "immediately" like in other engines) while forces are applied over several substeps, and cleared at the end of the step unless the force is persistent.

Since "Velocity verlet" needs to recalculate the forces

It needs to apply the force at each substep, yeah, but the force itself is constant across substeps. IIRC Velocity verlet is essentially just this:

// Not including angular velocity or rotation here
pos += vel * dt + 0.5 * acc * dt * dt;
vel += acc * dt;

where acc is force / mass (or more commonly force * inv_mass for efficiency).

(Note: In 3D, the inertia tensor depends on the orientation of the body, so for accurate results you may want to recalculate it before applying external torque)

Am I correct in thinking that I essentially need to move all the solver-systems into the SubstepSchedule?

The actual integration logic should happen in the SubstepSchedule, yes. However, it might be tricky to get Velocity verlet working nicely. The high-level scheduling for the solver in Avian (and most other engines) is roughly like this:

1. Prepare solver bodies
2. Prepare constraints
3. Substepping loop
    1. Integrate velocities
    2. Warm start
    3. Solve constraints with bias
    4. Integrate positions
    5. Solve constraints without bias to relax velocities
4. Apply restitution
5. Write back solver body data to rigid bodies
6. Store contact impulses for next frame's warm starting

Notice how velocity integration and position integration are separate, and the order is also the opposite of Velocity verlet. Avian uses an impulse-based solver where constraints are solved using impulses, and I believe the order is pretty important here.

#

Most cases where I've seen Verlet integration schemes being used are for stuff like n-body sims or chains, and various position-based solvers, which is kind of different from a typical game physics engine with an impulse-based solver

vestal minnow
# vestal minnow A lot of the integration and force logic will be *very* different with the upcom...

the force itself is constant across substeps
Or I guess this depends on what you're doing. Typically with physics engines you can apply an external force in e.g. FixedUpdate, and then that one force is applied over several substeps. But if you have your own custom integration logic, and had some n-body sim, you could technically recompute the force at each substep if you wanted to for more accuracy. It's just more expensive

little maple
#

I know trimesh colliders has the hollow problem and can make collisions inefficient, but can someone remind me what collider shape/type is the most efficient for collisions?

visual sparrow
#

(Talking out of my hat here)