#Avian Physics
1 messages ยท Page 34 of 1
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
You mean have independent triggers for sensors?
Yes
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 :)
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 ๐ค
oh crap, another thing...shape casting won't trigger this event right?
no, it won't
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
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
The point of sensors is to act as volume triggers and detect when entities enter or exit an area. You cannot do this with normal colliders that are attached to rigid bodies
Similar to Area2D/Area3D in Godot or triggers in Unity
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.
That gets the RigidBody of the entity that the event was triggered for, but does not work if the collider is a child collider
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
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
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
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
And triggers in Quake mapping, which matches intuition for people using TrenchBroom for Bevy ๐
I think thatโs fine
I also think thatโs fair, as long itโs consistent
Very, very relatable ๐
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
Yeah I think I like this
https://github.com/Jondolf/avian/pull/718
diff is nice +81/-113
turn chaotic evil and rename it to rbdy
How can I stop the auto-calculation of center of mass when using ColliderConstructor with TrimeshFromMesh?
imo rb is fine for internal names but not for something we expose as a property to users
NoAutoCenterOfMass on the root rigid body I think?
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?
Correct, though I'm wondering if maybe they should be affected
In general engines don't apply damping to kinematic bodies
i'm kinda neutral on this, but i use body component for hp/armor/etc and mind component for mp, and that makes me wonder how many people also use generic "body" component for their stuff
But really it's just "apply exponential decay to reduce velocity over time" which isn't necessarily behavior that must be limited to dynamic bodies
Oh wow this looks really fun ๐
@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)
Consider joining the character controller working group server we started yesterday
https://discord.gg/b4xs9aYY
One of the methods we're considering for the plane solver is the one used by that Unity KCC
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 
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
Yeah, KCC is fundamental for almost any 2D or 3D game.
I actually stopped developing my game in Rust just because itโs missing that feature.
I canโt stand Unity or Unreal (ugh, Blueprints!), but they both have really solid KCC implementations
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
Oh hey, I used that KCC before! It indeed works quite well ๐
agree, It works even better than default Unreal controller )
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
Waaay cheaper than convex decomposition but not like super cheap
Asset processing for scenes when 
Convex decomp is at the level of "prohibitively expensive" so that doesn't mean a lot ๐
Oh, I didnโt know! I suppose using trimeshes for those cases would be cheaper to setup but more expensive while running the simulation?
Honestly if we had more "working groups" and people collaborating on things, it'd almost make sense to have a dedicated server for Avian (some people have asked for this). But in general I would prefer not to officially "split off" Avian stuff from the Bevy server
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
Maybe repeat that request now in #community
@vestal minnow is my understanding broadly correct here?
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
Alright, so it would probably make sense to have some kind of HashMap<Handle<Mesh>, Collider> to only calculate that once per mesh, right?
Since in my case, itโs not 100 distinct meshes, but like 10 meshes each 10-15 times
Maybe ๐ค This feels like something we'll really want a first-party solution for but we might want asset preprocessing stuff first
I think asset preprocessing should be able to do this today
If we can get one of the three people on earth who understand it to implement it
also just checked and Godot also has the collider generation options in the scene import view
the real-time visualization is cool
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.
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
probably all custom components added to an asset would be readded when asset is reloaded
@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.
I know Jolt has an emphasis on being able to load / unload things in the background and preparing batches of physics bodies on a background thread, it's probably very important for them since it's literally used in Horizon Forbidden West
Currently the Sleeping marker component
Oh and they have stuff like allowing bodies to be sleeping when created, and neighboring bodies not being woken up when bodies are removed, to allow loading / unloading content without accidental wake-ups
Thx ๐
Even more renames
https://github.com/Jondolf/avian/pull/719
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
Hello, I am curious what would be the easiest way to filter out sensors when doing a spatial query ?
Putting the sensors on a layer that doesn't match your query's mask
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
It is now with the working group we started ^
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 ๐
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?
this is a ghost collision, the edge that is buried in the ground gets detected as a wall
if fix_internal_edges doesn't work then you can try using primitive colliders that extend sideways into the ground instead
fix_internal_edges is a trimesh setting, correct? I'll try converting it to that. Thank you
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
Iirc Jondolf was talking about making release notes for the new release, so I assume a new release should be getting close ๐ค
I've found a lot of third-party crates have a table tracking versions against bevy like this.
main is pretty much usable right now, I assure you ๐
yes = I am using it.
just my silly preference is to use a numbered / published version, because it somewhat makes my code more reliable
You can specify a commit sha in the cargo instead of branch if you wanted to pin it more explicitly btw (otherwise it's only when you run update or whatever triggers it to update the lock file)
It shouldnโt be very expensive. This might give you an idea, with 2048 vertices easily running on an old machine many years ago.
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.
Oh that great to hear, thanks ๐
I'm seeing propagate_transforms_physics vary in duration from 213ยตs to 2.1ms while I have physics time paused.. is that unusual?
I recall someone else mentioning something similar too, it's unexpected yeah
it may have been me a few days ago ๐ I did a little more analysis of my traces
unexpected as in "I'm not sure what's happening there, but it's very possible it's a problem in Avian?"
Do you mean it's 213 ฮผs when running physics, and then for some reason increases to 2.1 ms when paused? Or just that it varies in the 213 ฮผs to 2.1 ms range even when physics is paused
the latter
I made a thread here where I've been adding some notes on it
#1367307808558546984 message
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_assertfeature 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()
);
}
}
when the
glam_assertfeature 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
Oh ... I ran into that and didn't report is, sorry 
But I don't know why either of those would crash, radians just calls f32::sin_cos which surely should produce normalized values
yeah I'm having a hell of a time trying to narrow it down further lol. I'm guessing my system ordering isn't correct because I can't reproduce the panic early before the integrate_positions system runs
and fast_renormalize is specifically meant to normalize the value
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
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 
I second you
It would also allow other (I think, future) crates depending on avian to release newer versions
Also, a release to crates.io is more stable as it's always useable (the github might break thus breaking all projects depending on it)
narrowed it down to something in warm_start. might be from overlapping colliders and invalid normals

ContactManifold {
points: [ContactPoint {
local_point1: Vec2(NaN, NaN), // <-- wat
local_point2: Vec2(-0.15326612, -0.9881849),
penetration: 1.0,
normal_impulse: 0.0,
tangent_impulse: 0.0,
feature_id1: PackedFeatureId(3221225473),
feature_id2: PackedFeatureId(3221225472),
}],
normal: Vec2(0.15326612, 0.9881849),
friction: 0.5,
restitution: 0.0,
tangent_speed: 0.0,
index: 0,
}
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?
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?
If I can find a way to reliably reproduce this in a cleanroom I can submit PR with a test for it but for now I'm just leaving that snippet here unless @vestal minnow if you think the diff is worth applying
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
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.
Ah thanks so much ๐ I was looking in all the wrong places
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:
- 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.
- 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!
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
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
Ping @royal belfry
Hey sorry for the late response. When this happens I've found Project > Invalidate Cache (or something similar) and select everything and then restart will fix it. It'll force RustRover to reindex everything and update its own cache.
Thanks, will try. But you're able to use avian main branch using rust rover?
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
Still doesnt' work after invalidating caches; maybe it's related to something like https://youtrack.jetbrains.com/issue/RUST-13211/Opening-a-workspace-package-as-a-separate-project-leads-to-unresolved-imports-errors-for-local-imports
Probably yeah ๐ค which components would this be for? Position, velocity, and forces?
and can't you ease the internal values instead or is there a reason the component itself needs to be eased
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
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
I would start with Position/Rotation, not sure if the others are needed
Yeah those are fine, I can make a quick PR
it's ok i can make the PR
Definitely a weird issue, but hopefully it gets fixed. Apologies for not really being able to fix it though!
Hey everyone, how do I perform a spatial query / raycast without adding a component to an entity?
You can use the SpatialQuery system param to query against existing colliders
Thanks @cinder summit, I just figured it out
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
)
good luck ๐
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
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
is it something you're dynamically changing the shape of? Otherwise, you could make a mesh in blender and make a collider from that?
yup, customizable in editor.
i guess i could just build a mesh with that though, it'll probably be better if i want to actually visualize the shape for debugging ๐ค
using primitives might be better for expanding AoE though
wow, I've been using main for a while now and did not know about all these features
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
Right, it's responsible for automatically attaching colliders to rigid bodies, though things should work even without it if you insert ColliderOf (similar to ChildOf) manually
I updated the migration guide to be a bit more clear here
hmm.. conveyor belts would be tricky if you're using a floating controller, huh ๐ค
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 ๐ค
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?
This is amazing, thanks Jondolf
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
with_length_unit only appears to affect tolerances, and not actual simulation size. here's what I mean - left is scaled down 1/24 with camera zoom, right is actual size without zooming
with_length_unit tries to make physics work exactly the same with a different internal scale, what you might want is Gravity
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
just put it in a const at your crate root
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
twenty halfspaces
just a trimesh
So generate from mesh?
yeah, what are you trying to make anyway?
#general message
yeah, just make trimesh then xD
The only hollow shape that Avian supports is a trimesh, or an approximation where you place a ton of convex shapes at the border to emulate the border of the sphere
Hi all ๐ is it possible to toggle the Physics Debug Plugin's gizmo render at runtime with a resource or something?
You can use ResMut<GizmoConfigStore>, call config_mut with a PhysicsGizmos generic, and toggle the enabled property on the gizmo config
I mean ... Avian does support more, you can basically have whatever colliders you want by either having custom ones in parry or a custom collider type ... I'm not sure if making hollow colliders is particularly viable in parry though
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 ?
shape_hits iirc
thanks a lot
maybe using the cast terminology would make the discoverability better, since cast_rays would be it's ray analog
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)
oh yeah i forgot mb
It would also make it a bit more explicit that the single-hit version specifically returns the closest hit, and that the multiple-hit version might not (the hits are not necessarily ordered by distance)
True, should have said out of the box ๐
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.
#1370535365311598623 message
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)
I could be wrong but I thought child colliders propagate to the parent?
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
Wouldn't hurt!
So I think I've narrowed this down to malformed meshes. Like not watertight or overlapping triangles.
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)
collision hooks on both that insert collisions on the other?
Can you "insert collisions" like that? I had figured you'd have to do something using the external force components or something. And even then I'm guessing that would probably delay the solver by 1 physics update
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();
}
}
@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
it's TransformEasingSet from bevy_transform_interpolation
I know, I just don't want to add a dependency in my PR
Yeah it looks like it's not currently re-exported from Avian
Should I PR?
Yup that'd be nice ๐
the other re-exports are in src/interpolation.rs iirc so it'd go there
I always feel so guilty when I completely delete that nice PR template you made ๐
copied from Bevy
I should probably update it though, Bevy's has changed quite a bit
I almost never use the Changelog section lol
Oh lol it's not part of the prelude
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
I haven't, no
Can I PR it? I'm really curious ๐
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
No clue, but I have heard that a few other projects are using it
Just noticed you don't run doc tests
I'll add that as well while I'm on it
pretty sure it is running those
note that this is also the reason why nextest does not run doc tests
So I'd have to add that line anyways
I've at least had CI fail on docs many times
But did it ever fail on something like
/// ```
/// assert_eq!(1, 2)
/// ```
IIRC you only get compilation errors without --doc
these are all (or mostly) doc tests
huh, fair enough
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
ah cool
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
What are we currently doing? Saving a separate cache for each PR?
exactly
yeah I guess that'd be good to fix too then
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)
Bad news, looks like there's no cache at all ๐
There is a cache sometimes... It works on some PRs but not others lol
๐
Let me fix that for you real quick as well
(I had to deal with this recently)
At least system sets will need to be renamed, probably some other stuff too
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
Baah running into the good old issue of the linux runner having no GPU
let's fix that too
Need another approval on https://github.com/Jondolf/avian/pull/728 since I hecked up ๐
@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 
yep
I'm probably just in the wrong directory or something
I've had this a couple of times spuriously for some reason
I have no clue why that test in particular does not want to run on linux
I'll just disable linux for that one
@vestal minnow https://github.com/Jondolf/avian/pull/729 is ready
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
Yeah that's fine for now
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
now Windows is failing ๐
I think it just doesn't allow that syntax for env variables?
oh right
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 ๐
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
Doesn't sound particularly statistically significant ... Cache wise these behave largely the same
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
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 https://github.com/Jondolf/avian/pull/729 is through! Could you ping me when you merge it?
@visual sparrow Merged!
Cool! After the build on main is done I'll do a fake PR to test the performance ๐
yeah using SolverBody as a component cuts things down quite a bit :P
Here's a follow-up: https://github.com/Jondolf/avian/pull/730
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)
hmm I'm a bit iffy on including the lockfile but we can try it and see how it plays out
Yeah it took some getting used to for me too at first. But at least for me, it made maintenance easier in the end
I don't want it to pollute diffs but I guess it won't generally have too many changes after the initial commit
Yeah, and the changes does have tend to actually be meaningful
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?
well it's failing tests now ๐
oh it's the benches
Yeah, those weren't looked at at all before
I guess we can exclude them from the tests
I'll still let clippy run over them, though
Passed now 
Merged! I really appreciate you doing this, the CI was painfully slow
Let me know when you're ready to drop windows and I'll make it 3 times faster ๐
No problem ๐
Now let's test this; https://github.com/Jondolf/avian/pull/731
phahahahahaha
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?
Hmm still getting some recompilations I don't like, let me experiment on that PR
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
Yeah, took me a while to figure out they were getting aligned. ๐
It would be awesome if the relative offsets could be done "automagically". Looking forward to that ๐
Interesting idea. I'll give that a try. Thanks a lot ๐
@vestal minnow alright, this is the last one for today: https://github.com/Jondolf/avian/pull/732
after this, I'm happy with the CI for now ๐
Turns out this wasn't an Avian bug, but a required components bug??
Made an issue for an opt-in cache-free solver for easier rollback, would be interesting to try at some point
https://github.com/Jondolf/avian/issues/734
Solver bodies!
https://github.com/Jondolf/avian/pull/735
I'll probably work a bit more on the PR description and polish the implementation, but wanted to get the PR out finally
@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:
Having as many platforms as possible covered is mainly for cross-platform determinism tests
ah yeah that makes sense
14 min is very workable compared to 34 min ๐
oooo I want to try this out.. you're saying this will improve scenarios where there are many collisions? I'm slightly oblivious to what a "solver body" is
Testing would be very much appreciated! The SolverBody is basically Box2D's b2BodyState, which is an optimized representation of the body's positional state and velocities required for the performance-critical solver. It's also kinda similar to Rapier's SolverBody and SolverVel stuff, though with a very different representation
It's also optimized for fast conversion to and from SIMD types once we implement that
@vestal minnow there you go: https://github.com/Jondolf/avian/pull/736
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.
restitution can control how bouncy something is
https://docs.rs/avian3d/latest/avian3d/dynamics/rigid_body/struct.Restitution.html
A component for restitution, controlling how bouncy a rigid body or collider is.
Thanks
Mass property functionality for rigid bodies and colliders.
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;
}
}
@vestal minnow https://github.com/Jondolf/avian/pull/736 is ready for merge now ๐
Is there any other cache except normal_impulse/tangent_impulse in the ContactGraph?
Shaved 2 more minutes off the CI, now 12 mins on Windows 
Don't forget to add linux to the required checks ๐
Good call, added now
I don't think there's anything that'd be particularly relevant for rollback
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
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();
@vestal minnow I'll get an issue opened with the details unless you know of an existing one this is related to?
What exactly breaks here? Collisions work fine in my test case with empty children![]
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
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;
};
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
alternatively could maybe use a marker component to basically say "im handling the transforms myself"
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
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?
looks like z-fighting
try making collider a little bit taller
I'm generating the collider from mesh do I need to still modify it?
A component that adds an extra margin or โskinโ around Collider shapes to help maintain additional separation to other objects. This added thickness can help improve stability and performance in some cases, especially for thin shapes such as trimeshes.
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
that's not the first time i see it, but i don't remember how it was solved
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 ๐
thanks for trying to help me though ๐
@vestal minnow might figure something out, this doesn't look like an expected behaviour either way ๐
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
the street and shovel meshes are both trimesh yes and the collider for both of them is generated from mesh
I tried spawning the blue mesh in a higher position but it still randomly moves on its own
changed the collider to convex hull for the blue mesh also
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)
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
yolo ๐
Sure, will do in a few minutes ๐
I can test it with this code, is not very intensive, but probably help haha
It behaves the exact same as far as I can tell on Foxtrot
Mind you, I don't hook into any collision event triggers, so I can't comment on that being broken
Was low key hoping it would fix this jitter, but it didn't 
(Note that the performance in that jittery case goes down by about 20 FPS)
But it's the same on version 0.3:
was planning on doing this tonight just having a busy day ๐ซ
Cool, yeah same is good here
The perf improvements would mainly be if you had like thousands of contacts, which Foxtrot proobably doesn't
I have a few hundred static colliders and dozens of dynamic ones
So no ๐
Ping me if I should try another thing ๐
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
Yeah I'll probably merge it tomorrow (or technically today, it's 3:30 AM
), 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
I'm always happy that we both seem to have a similar broken internal clock ๐
Or maybe you just have eternal darkness in Finland anyways so it makes no difference
I'm getting some before and after metrics
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
-# peck when
^ 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
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
Like the very exotic case of "A still-standing cylinder on a flat trimesh"
Woah that's cool
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
it is just the solver-bodies branch, right?
yeah
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
yeah you're not too bottlenecked on the solver either so the perf difference there isn't too big
yeah
in my scene where I get a 3x perf boost I have nearly 13,500 contact pairs :P
my bottle neck does seem to be the narrow phase.. is that kinda expected?
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
y..yah. I need to do a simplify pass at some point
or I guess convex hulls should be fairly cheap
but ofc not as cheap as 2D rectangles lol
gonna redo my game with 2d rectangles ๐
What's the snippet for that UI?
Diagnostics support for tracking physics timers and counters. Useful for profiling and debugging.
yeah, it's the cool diagnostics UI Jondolf made. The "Score: 0" is from my game and before you ask, yes I'm not good at my game ๐ญ
hehe
Ooooh TIL! Will implement that later into my Physics Debug view ๐
oh, wonder if it would be useful to have counts of collider types on there too? might just be noise at some point..
does this support multiplayer with bevy_ggrs? as it needs to support rollback and be deterministic to be used
is there a hack for making it be friends with the FPS counter? ๐
But cool UI, will use that next time you ask for performance differences!
Avian should be deterministic but there's some special stuff you might need to do to get rollback working nicely, like disable the contact cache (set NarrowPhaseConfig::match_contacts to false) and maybe disable the SleepingPlugin since it has some timers that might not like rollback
yeah it's new in 0.3
https://joonaa.dev/blog/08/avian-0-3#physics-diagnostics
Okay I'm merging the solver body stuff now to unblock some things
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
Woah this is really cool to see ๐
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
if I was going to optimize my game by getting rid of the trimeshes... and given my bottle neck seems to be the narrow phase.. is it better to build my world mesh out of fewer/bigger convex hull colliders or more/smaller convex hull colliders? or is this even a good idea to begin with?
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
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
I just implemented a new faster version as well ๐ But I'd be curious to see how yours is different
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
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
Here's the minor gyroscopic motion optimization PR
https://github.com/Jondolf/avian/pull/738
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
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)
Cubes have isotropic inertia tensors too!
In local space but not necessarily in world space, so they still experience gyroscopic effects right?
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
Anything with two intersecting axes of threefold or more rotational symmetry, is apparently the condition (though the link to the reference has died) https://physics.stackexchange.com/questions/514554/isotropic-moments-of-inertia
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
Found it with the wayback machine
https://web.archive.org/web/20211003110410/https://www.ipgp.fr/sites/default/files/archimedeani_200115.pdf
Yeah, it's very unintuitive - from what I've seen the literature likes to call bodies with isotropic inertia tensors (i.e. all 3 eigenvalues identical) "spherical tops", ones with a single "special axis" (like capsules, 1 unique and 2 identical eigenvalues) "symmetric tops" and shapes with 3 distinct eigenvalues "asymmetric tops"
Which does make it sound like only spheres are spherical tops, even though anything with the right symmetries will be
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
do your printlns fire?
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]
you should really cache the mesh & material btw
take out the as_vec3 and pseudo-splat, there's an impl for Dir3 * f32
thanks
not sure if vec3*vec3 actually does something weird
I can println the velocity real quick
why add the grenades as children of the player?
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
if you're on 0.16 there're custom relations
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.
does it work without the hierarchy
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
... why are you doing Transform::from_scale(position)?
You know what. I don't remember why
gonna poke your enemies with the other end of the grenade
I think I got it
Thanks for the paper btw, I'm curious to see how well it performs. The main things I care about for gyroscopic motion are (1) the method is stable and doesn't introduce energy into the system over time, (2) performance, and (3) reasonable accuracy for game physics
Having better momentum conservation would be great, as long as it doesn't hurt perf in any meaningful way
Ok, now it seems like I'm spawning two grenades at the same time.
one based on player
one not
ww
you're iterating on all your queries, of course one of them's going to happen twice
ui camera if i had to guess
I'll have to use the With<PlayerCamera> tag
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.)
excited to add gyroscopic torque to my game about rotating at upwards of 11000rpm
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
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?
i don't see how it would break, but doesn't rapier also use parry?
I'll look into this in a bit, probably just forgot some methods call somewhere
You can do that in both Avian and bevy_rapier, but if you're doing that a lot, it might have some additional overhead since internally compound shapes store a BVH to accelerate queries, but with a single shape it's kinda redundant
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
Works fine for me just trying various LockedAxes configurations in the dynamic_character_3d example ๐ค
I'm using the main branch (since the solver body stuff was merged already)
hmm, I tried that out and it works as expected... I'm doing LockedAxes::new().lock_rotation_y().lock_rotation_x(), so.. can still rotate on the z axis... and the dynamic controller behaves correctly... just my game doesn't.
I'll keep looking at my game code, might be something else going on
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
does the paper have measurements of a real-world example in it? which of those gives the more accurate period?
Nice, looks pretty cool! My main concern would definitely be performance, this (in the current form) stores an extra tensor and two vectors for the solver bodies, which isn't ideal, and also adds some extra overhead to integrate_positions
some of that could probably be refactored to be a bit more optimized though
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
I don't know how to describe it, but what you're saying sounds like something I'm experimenting with
like... a constant forward force
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
yeah, I'm just applying a force every frame in that clip
Awesome
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
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_impulseapply_linear_impulse_at_pointapply_local_linear_impulseapply_angular_impulseapply_local_angular_impulseapply_forceapply_force_at_pointapply_local_forceapply_torqueapply_local_torqueapply_linear_accelerationapply_local_linear_accelerationapply_angular_accelerationapply_local_angular_acceleration
(we could also have something like Unity'sForceModeif 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
looks like adding bool for local would remove half of them
is the signature different between local and global?
So you end up needing to store at least
- World-space increments for linear and angular velocity
- World-space forces and torques
- Local forces and torques
- 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
The _at_point methods don't have local versions atm since it's less clear what the point should be relative to
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
i assumed that it does, not entirely sure though
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
gahh I of course can't do this with the QueryData approach, I obviously can't add components with that
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
- Use a
QueryDatatype and eat up the cost of storing both forces and accelerations in both local and world space for every dynamic body - Use a custom
SystemParamthat 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
is there overhead to making it a required component?
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
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
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
neat
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.
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
or maybe just upstream it to bevy, splitting transform into real(FixedUpdate) and visual(Update)?
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?
what's your coefficient of restitution
A component for restitution, controlling how bouncy a rigid body or collider is.
I'd probably rework some of the API and how the components work internally, but we could consider splitting them out to their own crate, yeah
I'd maybe rename them like GlobalTranslation2d/GlobalTranslation3d and GlobalRotation2d/GlobalRotation3d
which then write to the GlobalTransform if present
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).
What is a y_sort system? ๐
I guess I just meant a system that mutates my Transform z based on y
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
Are you using transform interpolation?
Yes
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
Ok, great thanks!
I have this PR open that would technically fix it, but it seemed to cause other problems for someone who tried it so I haven't merged it (yet)
https://github.com/Jondolf/bevy_transform_interpolation/pull/10
so i see RigidBodyQuery
is there a way to tell it to exclude static bodies?
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)
looking at your code you dont ever use it lol only a ReadOnly version of it
i figured i should use it since im implmenting arbitrary gravity positions
bashing my head atm tho
since it apparently hate mut with a passion
idk how to apply a force to the body since type system has a meltdown over the normal way to get a mut query
works fine without mut tho
with QueryData types you don't use &mut at all, just use Query<RigidBodyQuery>
then how do i get mutable access?
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
what should i use then?
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
well its something
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
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?
Maybe I should just fork and impl my change where gravity is normally calculate?
did i steal the dynamic controller example? yes
does it mostly work? yes
need to rotate with gravity and make directions local space based prob...
wtf is rhs?
right-hand side
in the parameter list at the top
so this is a version of rotate around y?
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
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
prepare for forward/right to sway uncontrollably
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:
Solved: I was manually swapping rigid bodies and it was messing up the ColliderOf component
Didn't have that
Only issue atm is damping being in world space instead of local player space
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
oh and yesterday I ported the joint rework stuff to the new solver bodies on main... that was also fun
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
idk why
everything seems ok for a moment then suddenly im flying
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
seems to work the damping was really low but ended up just cutting out the formula for it and that brought it back into resonable level
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 ๐
^ 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 ๐
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
Avian has its tests already on all three ๐
In order to make sure that determinism across OS is working
I've got a fun ColliderConstructor rotation-based race condition if anyone has any insight on that topically ๐ https://github.com/Jondolf/avian/issues/741
connect the three runners over network in CI and have them play a game together, then log how much they each deviated ๐
Hehehe, I like that suggestion
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?
You can downcast the collider, I think it's something like collider.as_capsule().unwrap()
Yeah i saw the method very coolios
But I thank you for your answer
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?
is you compound a bunch of colliders together, does it prevent ghost collisions?
Observers are designed to be run asap
If you want to have a state check you'll need to pull it in with a system param Res<State<MyState>>
This'll let you grab the current state and check.
The addition of CollisionLayers as a required component of Collider really blew up my project. It's not really avian's fault though, I think it's a Bevy issue...
https://github.com/bevyengine/bevy/issues/19333
I'm curious if anyone else has ran into this
i really doubt it, but it might be worth testing
Yeah it's kinda annoying... For internal reasons every collider needs to have CollisionLayers now, so it definitely should be a required component, but it does cause problems like this
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
Jolt supports this through enhanced internal edge removal and I have an issue for it
https://github.com/Jondolf/avian/issues/612
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
tbh I think it makes sense for Collider to require CollisionLayers. To me, this is more of an issue with required component order logic. I don't think it's as unambiguous as the docs claim
To me, there is a simple solution around all of this. I proposed it here: https://github.com/bevyengine/bevy/issues/19300 but the implementation is probably non-trivial
If a component requires just for ABC to exist, it should never override a requirement of ABC::XYZ
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 ๐ค
is that a 25 tall pyramid (out of curiosity)
the graph coloring looks pretty fun at a larger scale like this
it's this pyramid ^
ah, 2d
yeah 3D works too but in 2D it's easier to see and test things
but yeah that looks like very good parallelism
wait I wasn't even running in release mode ๐ though I think I have some debug optimizations enabled
-# 100
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
Crysis barrel stresstest
kinda satisfying
Certainly a better use of electricity than running AI
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 ๐
Wouldn't even have performed at all 
Oh yeah that version would have been perfect for the "impending boom" jam theme
surely you won't mind if we use bevy_xpbd 0.1 and Bevy 0.10 for the game jam, right?

I guess that's a good plan if we also needed to support a theme like "blast from the past"
at least Bevy 0.10 added "stageless" scheduling aka schedule v3
working with pre-stageless would be fun
bevy_xpbd 0.1 was basically unusable though ๐ญ
I've been gone so long that xpbd is renamed. avian is more memorable. so good change.
It's more than just a rename too, it behaves much better now!
Really satisfying, I don't understand how there is some gaps when it falls though
The paper mentions never voiding internal face contacts, but this is useful to resolve penetrations that can get stuck bouncing between opposing faces until they pass through. This was my motivation for fixing this in my physics.
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
Controls how gravity affects a specific rigid body.
though it seems like you'd want parallel identical simulations
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
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
Yeah someone opened an issue about this last week
https://github.com/Jondolf/avian/issues/739
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
RemovedComponents<T> gets cleared at the end of each frame iirc
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 inFixedUpdatethen that's not true :P
I mean you can clear EventReaders manually and break that behavior
Or I guess rather clear the Events
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
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
mm I should probably solve the "more than 4 points" problem by doing manifold reduction
so
- Perform manifold reduction to get a maximum of 4 points per manifold
- 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
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
The Four Color Map Theorem (or colour!?) was a long-standing problem until it was cracked in 1976 using a "new" method... computers!
A little bit of extra footage from this: https://youtu.be/laMkuPrad3s
This video features Dr James Grime - http://jamesgrime.com
More Grime videos: http://bit.ly/grimevideos
This video supported by Squarespace:...
whoa that's interesting
Yeah it's crazy what kind things you can learn with the right abstraction
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.
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?
You can make a HashMap<Handle<Scene>, Collider> or similar and cache your results there
It would be nice to have some asset preprocessing to do this before even launching the game, but thatโs not builtin yet
Thank you.
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)
this seems unusual to me.. how many is "a bunch"?
~60k
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?
I'm guessing the problem is that the current sweep-and-prune broad phase needs to always iterate through all AABBs, even for static bodies. It does early-out for static-static intersections, so they don't make it through to the narrow phase, but it still needs to iterate through them and do those checks, which causes overhead even when everything is static and nothing is really happening.
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 ๐ค
awesome!
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
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
That's super helpful, thanks!
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?
Ah just found: ColliderOf that solves it ๐
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?
@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 ๐
What would this look like in practise?
#[derive(Debug, Resource, Default)]
pub(crate) struct ColliderCache(HashMap<Handle<Mesh>, Collider>);
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
Yeah that sounds good
Helo everyone
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
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),
));
}
}```
We could just add some helper to ColliderConstructorHierarchy to enable/disable events for the objects, but I'm not sure I love how we're adding more and more properties to it :/ I feel like we should have a nicer and more general way to insert components to entities spawned from scenes
@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
yeah
You need CollisionEventsEnabled for the collider entity
A marker component that enables collision events for an entity.
Thanka
You cool
@vestal minnow there you go: https://github.com/Jondolf/avian/pull/743
Objective
Creating colliders from meshes can be expensive when not just using a trimesh
Especially convex decomps take ages
In end-user cases, ColliderConstructor and ColliderConstructorHierarchy ...
But there's a tiny issue
uhhhh
I don't know whether that is just what avian's main does right now or whether that's my PR 
I checked the generated colliders and they look the same visually
at least, nothing that is obviously wrong
Let me check main real quick
Yep, it's avian's main ๐
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
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
If you give me a commit, I can check that one specifically
on it
damn, that's pretty nice
right? ๐
works
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
yeahhhh I can probably repro this by just running foxtrot on Avian main right?
Only thing I changed was
avian3d = { path = "../avian/crates/avian3d" }
So yeah ๐
(with my fork being up-to-date with avian's main, of course)
cool, I'll try to look into this later today then
@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
ColliderConstructorinto 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
ColliderConstructorso that it isHashand put it in the key
Your choice
hmm right that's kinda annoying
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
3 is probably fine for now
๐
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
Yeah that's out of scope for this PR
the size of ColliderConstructor isn't much bigger, I think it's just two more vecs for ConvexDecompositionWithConfig
(which would be empty here)
@vestal minnow done ๐
You'll be interested in this too ๐
https://github.com/Jondolf/avian/pull/743
(in case you're wondering, this is a release build, so it should be representative)
this is all automatic? I don't have to change anything in user code to take advantage of it?
If you're using ColliderConstructor and / or ColliderConstructorHierarchy, then this is automatic, yes ๐
Which I believe is what most are already using for generating colliders from meshes
oh geez, I'm not most, I need to fix that
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 secscargo nextest: 7.16 secs
where nextest also gives nicer output
@vestal minnow I opened https://github.com/Jondolf/avian/issues/744 so you have something to put in the milestone ๐
https://github.com/user-attachments/assets/a6e183f9-b5a6-44cf-96ed-e0822596394c To reproduce, take Foxtrot and patch it to avian's main branch
not sure if this is related, but I've been sticking to 71b835c (the commit just before solver bodies) because my collider (I believe it's convex decomposed) started ignoring LockedAxes... I wonder if that's why I couldn't reproduce the issue with the dynamic character controller example ๐ค
Does this also happen with common colliders like spheres and so on?
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?
@visual sparrow I'm guessing this is also new 
now how do I start to debug this...
oh now the lantern is a beyblade
phahahaha yeah I did not have that before
But ngl it looks really fun
video plz
That must look nice with the dynamic light
Oh wow at around 35 s it's even on its tip!
New jam mechanic?
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
probably
The floor collider is a convex hull FYI
in case that matters
it's the new gyroscopic torque I think :/
that would explain the spinning
It's probably worth it for the cool tennis racket effect though ๐
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
Setup in the OnAdd trigger:
commands.entity(trigger.target()).insert((
ColliderConstructorHierarchy::new(ColliderConstructor::ConvexDecompositionFromMesh)
.with_default_layers(CollisionLayers::new(CollisionLayer::Prop, LayerMask::ALL))
.with_default_density(10_000.0),
RigidBody::Dynamic,
SceneRoot(model),
));
(BIG density, I know)
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
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?
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
I think Jolt does something that looks like that because it uses the eigenvalues + rotation representation of the inertia tensor?
Yes that, beat me to it ๐
that makes sense
You two figured this out reeeal quick ๐
Glad my absolutely-not-minimal reproducible example was of help lol
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)
right it should be
delta_rotation.0 =
Quaternion::from_scaled_axis(*angular_velocity * delta_secs) * delta_rotation.0;
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
The practical difference is that right-multiplication applies the rotation in the local frame and left-multiplication in the global frame right?
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
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
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
yeah
okay fixed here
https://github.com/Jondolf/avian/pull/747
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 ๐
beyblade feature flag when tho
I'll add a nostalgia feature flag that adds bevy_xpbd 0.1 behavior
Ah, then you can probably already close that milestone issue ๐
I think this fixed my rotation issue as well. Re-checking main..
nope, I guess something else may have fixed it ๐ค oh well, I don't see the issue I was having at least
maybe i should open an issue on nextest to support --cargo-profile or w/e
but yeah nbd for game jam, who's writing unit tests
@vestal minnow I'm serious ๐
If you have time, I'd like to see the results.
Trying this in a bit, making an issue for the Position thing
yeah that has higher priority ๐
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 
oh there's some rand stuff that probably messes up archetypes and iteration order or something
maybe? idk
Whew thatโs a relief ๐
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 ๐ค
Yeah the resulting archetypes are entirely random. If the behavior is different, it will point towards your implementation right now being ambigous to begin with
Please don't use this for real
Also, is it really "messing up" archetypes when massively blowing them up is the entire point of the crate? ๐
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
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
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?
Primitive sphere, I suppose?
For more complex meshes, I think convex hull
(Talking out of my hat here)
