#Avian Physics
1 messages Β· Page 37 of 1
i looked more into the nesterov stuff not being faster and i think its just a matter of initial conditions and test case
its correctly implemented
it probably only helps on complicated convex hulls
but we should just do the gauss map traversal thing for that anyways
i have too much work to do on bevy itself to start grabbing random issues tbh
unless theres something I specifically would be more of a help than others on, im inclined to not
Fair enough π
ive got a renderer to fix lmao
You fixing stuff for bevy?
dang, I didn't know this was one of the goals of avian. That's really cool
I definitely know a lot more about physics than I did before using avian π€
Yeah π―
Collision detection stuff for Peck (or otherwise) is one thing you'd probably be great for, but you're already kinda doing that with datura :P
Other than the GJK+EPA stuff, some things that I still need for Peck that are related to collisions and you could maybe be interested are
- convex polyhedra
- triangle meshes
- port Parry's VHACD implementation to use Glam/bevy_math or to be math library agnostic. This can/should be its own crate separate from Peck
- and/or implement CoACD in Rust :)
Also it would be interesting to try Erin's approach to polygon-polygon contacts using a kind of SAT+GJK hybrid without any EPA (see https://discordapp.com/channels/691052431525675048/1203087353850364004/1392866053951852698), and Dirk's polyhedron collision algorithm with this fancy new optimization idea (see the thread)
- and/or implement CoACD in Rust :)
For large or complicated Avian work like the joint rework or solver optimizations, I tend to be quite wary of delegating work to others since I have ideas of how I would like to try and implement those, and I don't want others to spend a lot of time working on something just to have me turn it down or rework it again
I think one thing that I could definitely delegate more to others (anyone!) is to have more/better examples for Avian. We have a lot of features that don't really have their own examples, some examples could be merged (like a lot of the joint examples), we should have more examples demonstrating how to implement common but challenging physics-y things (buoyancy, fractures, vehicles), and so on
For this I think it would be a good idea to first make a list of examples that would be useful, and how they would be structured in the repo, rather than just adding examples for whatever randomly comes to mind
I Just want see Avian merged into bevy, jondolf is goat
im eager to see the api work that allows for interpreting spheres as points with radius etc
not really sure how you're going to handle that translation
the exploratory no-EPA gauss map optimization thing would be cool to bench against datura
i wonder if the extra bookkeepin ends up being worth it
maybe for complex shapes its worth it but not simpler ones
It works π I might've just cooked
Before:
fn apply_forces(query: Query<Entity, With<RigidBody>>, forces: ForceHelper) {
for entity in &query {
// Apply a force of 10 N in the positive Y direction.
forces.entity(entity).apply_force(Vec3::new(0.0, 10.0, 0.0));
}
}
After:
fn apply_forces(mut query: Query<Forces>) {
for mut forces in &mut query {
// Apply a force of 10 N in the positive Y direction.
forces.apply_force(Vec3::new(0.0, 10.0, 0.0));
}
}
@little maple This at least works properly with parallel iteration, you can just use par_iter_mut like normal
fn apply_forces(mut query: Query<Forces>) {
query.par_iter_mut().for_each(|mut forces| {
todo!()
});
}
I've updated the PR to this already, I'll probably merge soon
https://github.com/Jondolf/avian/pull/770
Screw it, merging now π I really like the API, and it's definitely a huge improvement over 0.3
yolo
lol I am only slightly dreading re-doing my vehicle controller to use this
Just checked it out, this is a really nice API. Good job!
Impulses can no longer be persistent. Use persistent forces instead.
Thank you this sliiiightly annoyed me haha
Big fan of the removal of the External prefix too π
Force rework, do we get the persistent vs non-persitent thing yet?
ConstantForce is persistent, forces applied via the Forces API are not
Ah, neat
Forces is essentially like Unity's Rigidbody.AddForce, Rigidbody.AddForceAtPosition, Rigidbody.AddRelativeForce, Rigidbody.AddTorque, and Rigidbody.AddRelativeTorque packaged under one dedicated type, using separate methods like apply_force and apply_linear_impulse rather than differentiating "force modes" via a ForceMode enum
And the components like ConstantForce are for persistent forces like the ConstantForce component also found in Unity's ECS physics
I was also considering calling ConstantForce like PersistentForce since it might be more accurate, but "constant" is cleaner/shorter, used by other engines (Godot, Unity) and maybe more commonly used as a term in actual real-world physics too
even though it's not truly constant since users can modify it
Oh right I can now also close the PR about CustomGravity since ConstantLinearAcceleration fills that purpose
https://github.com/Jondolf/avian/pull/666
oh yeah, didn't you say that the forces might be slightly different? Like... should I expect to use the same amounts I'm currently using?
I didn't notice a difference when testing but someone else said they did
At least it seemed like I was getting correct results, e.g. with a gravity of 9.81 m/s^2, a 1 kg cube stays floating when applying a 9.81 N upward force, or an upward linear acceleration of 9.81 m/s^2, or when applying an upward impulse of 9.81/64 kgm/s every frame (with a time step of 1/64 s)
and increasing/decreasing mass seemed to affect things correctly
but testing would be appreciated π it could be that I still missed something
if you do see that values are different, let me know and I'll double-check
One important difference is that the point for methods like apply_force_at_point is now a point in the world, not a point relative to the body
Is that a unit test? Sounds useful
yeah, when I get a nice block of time set aside I'm gonna try this out.
That took a while lol
https://github.com/Jondolf/avian/pull/779
718 lines of tests π
mostly just the same thing duplicated for different APIs though
Good ideas in there π
looking at docs on main, there's a double bracket in the new force docs at dynamics/rigid_body/forces/plugin.rs:103
one of my hitboxes is hitting twice and i have no idea why
the exact same collision event happens twice, same entity and everything so it's not that i've made two hitboxes or something
Maybe it bounces slightly so they're not colliding for a frame in between the events? Not sure why it'd trigger twice otherwice
Unrelated, but currently we store raw geometric contact points local_point1 and local_point2 for ContactPoint:
// In 3D
pub struct ContactPoint {
pub local_point1: Vec3,
pub local_point2: Vec3,
// ...other stuff
}
However, for internal physics purposes, I nowadays only really need two world-space anchors anchor1 and anchor2 that are relative to the center of mass, so I would like to store those instead. But for users, that'd be more annoying to work with, and quite often people specifically want access to global contact points.
I think we could get the best of both worlds by having anchor1, anchor2, and also a global point that is the average of the two points in world space:
// In 3D
pub struct ContactPoint {
pub anchor1: Vec3,
pub anchor2: Vec3,
pub point: Vec3,
// ...other stuff
}
point would be purely a user convenience for debugging and for gameplay logic that needs to figure out the infamous "where did these two shapes hit?" without having to manually transform points and do the math yourself
(also surprise surprise, Box2D does exactly this lol)
Unless someone complains, I'll make a PR for this tomorrow along with optimizing some constraint preparation logic that is related to this
(which in turn is related to the wide SIMD stuff :P)
Also I might finally change penetration to separation or distance since it's expressed that way in literally every other physics engine, but not 100% sure yet
i feel like i've already asked, but is there a good way to get the force exerted by a collision? the kind of value you can use to play a sound or deal damage that scales with how "hard" something hits something else
I'd also be interested in that π
@vestal minnow I would like to get a number similar to the one described that allows me to check whether a piece of glassware like a bottle should be shattered by a collision or not
The normal impulse is available, but I think it's not actually the value user code would want, since it's a clamped accumulated impulse for solver purposes, not necessarily the full collision impulse across substeps with restitution and everything added on. Contact constraints already have a max_normal_impulse (bad name, should be more like total_normal_impulse) which I think is closer to what you'd want, we just need to expose it in user-facing contact data properly
hi, i think there's a bug in main now; kinematic bodies are affected by gravity
I can try to do that tomorrow as well
i had to add GravityScale(0.) to forcefully disable it
hah whoops, I swear I tested that before merging the force rework stuff π I'll try to fix it asap and then head to bed
should be an easy fix
haha okay, sorry for troubling and thanks for your amazing work π
I even had a TODO for this
// TODO: Do we want to skip kinematic bodies here?
we should really add marker components for dynamic/kinematic/static, it'd make these filters more efficient and easy
ah so like splitting RigidBody into ZST components?
yes plz, a lot of my code in Avian Pickup is just "if not dynamic return early"
Yeah possibly, or if we wanted to keep the enum API for whatever reason, we could make RigidBody an immutable component and automatically insert/remove a DynamicBody/KinematicBody/StaticBody component based on the variant
probably just the ZSTs and no enum though
(somehow i mixed up DSTs and ZSTs)
but some shared component would still be good so you can iterate over all rigid bodies without caring about the kind of body, like a RigidBodyMarker or something
or just RigidBody but we'd want a panic or warning if you only have that without DynamicBody/KinematicBody/StaticBody specified
i wonder when Query-by-value is gonna land, it'll be cool to see this kind of pattern supported first-class
I think we want to split RigidBody even ignoring query reasons, we want to have different required components for different types of bodies
Like static bodies don't need mass properties, forces, or velocity
But dynamic bodies do
Hot-fix here
https://github.com/Jondolf/avian/pull/781
@vestal minnow cmon jondolf, Its time to optimize It
So bevy can merge xd
Avian Will be the official one
well it gets despawned on contact, though maybe thats delayed
when using the normal despawn method, i actually get a warning because it tries to despawn it twice
thanks π Could you ping me when you get around to it?
Not urgent or anything, I just want to keep it in the back of my head
i think the delay could be it
nvm, i tried deleting it instantly and it still has two events
which event? reading them via EventReader?
ok so i had a pretty weird setup where i had a trigger getting the hits, then that would trigger another event. i just refactored that into a single function (which is pretty long but anyway), but it turns out the real problem was that i added the trigger observer twice. but now i've got another problem, for some reason if i despawn the projectile on contact, knockback doesn't get applied. i have no idea why, but i suspect adding a frame delay will fix it since thats the setup i used to have
it takes a few steps to actually apply force, my projectiles despawn when their velocity changes from initial + gravity
is there an easy way to apply a frame delay?
i think that might be the easiest option, and if it doesn't work i can try something more complex
idk what it does but i'll try commands.queue()
oh thats only for spawning
it's for commands that aren't the premade methods
there's actually a delayed command thing someone's trying to get into 0.17, i think?
yeah, i saw that issue actually
are there any current solutions
if nothing else i could make an event for it but that feels like overkill
a Local with a counter
whats a local
so what would i do with this?
set it at the end of your system and check it at the beginning
the system should only be running once though?
do you have run_if()s on it? or is it not in a schedule at all
no, it's triggered
oh, observer
might be better in the schedule, but you can spawn an entity with a struct (Duration, Entity) on it that you check for in a schedule system
i just want to delay for 1 single frame though
that feels like overkill
i guess i could use a system though
can put it in an event, but then you need to make sure the event reader doesn't run until the next frame
i don't think you want to delay it by exactly one frame anyway
might be related, but Avian crashes when i insert Disabled to an entity that observes OnCollisionStart...
i went back to the old version (before reworking stuff), deleted the one actual line that matters and now it's working 
i didn't like how it used a separate event but it looks like i've got to anyway
its annoying i wasted so long but at least it's working
hello, so i split a collider into 3 smaller colliders (as children of the main rigidbody) and now whenever i apply torque it rotates much faster than earlier,
anyone know what causes this?
@rough nebula you are applying the force on the 3 colliders
You need apply Just in one colliders that need be the center of the mass If you want it to go straight
hmm i am applying it to the parent object
mut query: Query<(
&mut Transform,
&mut ExternalImpulse,
&mut ExternalAngularImpulse,
&LinearVelocity,
&mut Ship,
)>,
Ship is the parent object
the one with the rigidbody
You are querying anything with externa impulse
no, im querying anything with an external impulse and ship
You need add WITH<Ship> on the query
Iam pretty sure
thats not how it works
Oke so try make the others colliders Mass 0
Also make they dont collide with each other
This is because the angular inertia of the body is calculated from the colliders.
You either need to calculate and insert it manually, or use the MassPropertiesBundle to calculate it based on your original collider.
i am adding mass to the main body manually
There is also an "angular mass", it's separate to "linear mass"
AngularInertia and Mass
I hated external forces, and this is so much better.
Though for nonlinear, cheap to compute forces I would still prefer hooking into SubstepSchedule with
car_body.linear_velocity.0 += impulse * car_body.mass.inverse();
car_body.angular_velocity.0 += car_body.global_angular_inertia.inverse() * r2.cross(impulse);
Maybe it's worth noting when and how to do it in the docs?
In general I mostly treat the SubstepSchedule as an internal detail that users shouldn't need to worry about. It's prone to heavy changes and has limitations in what you can do, and most engines don't even allow you to have logic that modifies body state there. For your example, on the main branch, you can't even modify LinearVelocity directly, you would need to use the SolverBody since that is used for the solver
Also impulses are traditionally not something you apply over several substeps, forces are used for that
anyone know if there's a significant performance difference between raycasts and shapecasts?
Probably not a huge one, but shapecasts are still a bit more expensive. It might depend on the shape too, raycasts have optimized custom implementations for more shapes
most engines don't even allow you to have logic that modifies body state there
Yeah, and Avian is better than most engines.
Please, don't hide implementation details from us.
I understand that it can and will break with updates. (Even public API breaks with updates, right?)
The line before that is let impulse = force * dt;
If you're not doing like thousands of them, I wouldn't worry about it too much, use whichever one is easier and fits your use case better
Yeah I will try to keep the internals there available so people can have custom logic like that for advanced use cases
The Avian may not be the most performant engine due to reliance on ECS performance, but it definitely can be the most customizable one.
nevermind, actually! this happens with despawning too. and on the buffered CollisionStarted event as well
is there a way to create a very minimal avian project (preferably headless) to test things out as a minimum reproducible example?
Just a basic Bevy app with DefaultPlugins is probably the easiest
You can set up a MinimalPlugins config too but IIRC there are some additional plugins you'd have to cherrypick currently, at least in 3D
for like scene stuff and whatnot, though we should make minimal setups easier
that's interesting... bevy with default-features = false has this amount of crates
and then it pulls this amount of crates when i add avian2d?
oof
-# yeah there is no escaping year-long compile times is there...
Is this Avian with default features?
yea- oohhh i see avian depends on bevy_scene by default
avian2d = { version = "0.3", default-features = false, features = ["2d", "f32", "parry-f32"] }
(or the main branch, though I don't think we've changed much collider stuff there)
and it crashes with root entities too -- should i open an issue report?
Yeah I can reproduce this with both observers and EventReader, though it only seems to happen with Disabled, not with ColliderDisabled or when despawning
yeah i think the crash on despawning was on my part--although i wasn't trying to despawn it anyway in real life scenarios
Oh I think I know why this happens lol
lmaoo
okay yeah the fix is to change this
mut query: Query<&mut CollidingEntities>,
collider_of: Query<&ColliderOf>,
to this 
mut query: Query<&mut CollidingEntities, Or<(With<Disabled>, Without<Disabled>)>>,
collider_of: Query<&ColliderOf, Or<(With<Disabled>, Without<Disabled>)>>,
waiting eagerly for Allows<T> π
Try using cranelift, the parallel compilation flag, and if youβre not on Windows, the share generics flag π
With<Disabled> overrides the default query filter?
yeah Disabled needs to appear in the query
cranelift has been really annoying to me because proc-macros straight up crash the server on syntax errors. i got the suggestion to use llvm for dependencies instead, but there's a bug with tracing that doesn't let me do that
I use tracing and cranelift for deps 
well, are you on windows? π
people were like "users won't have to think about special-casing logic for Disabled" meanwhile Avian needs to consider it in a billion places
Nope π that explains it
How come?
I would have expected it to just ignore everything that is disabled
(Asking mainly because I wonder if I should be considering it somewhere as well)
I mean it's not too bad but mostly just a bunch of OnAdd and OnRemove observers need to also consider Disabled to add/remove colliders from various data structures and to remove contact pairs etc. when entities are enabled/removed
And in those observers I sometimes need to be careful with queries to make sure they include disabled entities
For example this specific case was this
// Remove collision pairs when colliders are disabled or removed.
app.add_observer(remove_collider_on::<OnAdd, (Disabled, ColliderDisabled)>);
app.add_observer(remove_collider_on::<OnRemove, ColliderMarker>);
and remove_collider_on had a query that incorrectly didn't include disabled entities
To be clear, you mean that when a disabled entity is inserted, you need to make sure the OnAdd hook can access it?
meanwhile i came up with this crazy hack to remove all components reflectively instead of adding Disabled, and it doesn't even work cleanly because i still need to reset several things like linear velocities and sprite states
Wow, I've got tons of these ;-;
That means they all crash
Since I unwrap
Or I early return, which would mean that the components were not initialized properly
This is quite the footgun π
yuuup
Thanks for bringing this to my attention
my morning coffee still hasn't hit, I'm not understanding... is the problem that you were accidentally ignoring disabled colliders?
- When a collider is disabled, I need to remove it from the contact graph and constraint graph
- The observer system was querying for the disabled entity but the query didn't explicitly allow disabled entities
So if i get this correctly, each collider has its own mass (generated automatically with collider density) and that was used in the torque calculations instead of the mass i put in manually
While the mass i used was still used for forces because linear and angular mass is different? π€
Here's the quick fix
https://github.com/Jondolf/avian/pull/782
I can see the utility of having a common Disabled component but yeah, that seems like a source of tricky scenarios that are hard to reason about π€
I recommend reading the overview of the mass property docs
https://docs.rs/avian3d/0.3.1/avian3d/dynamics/rigid_body/mass_properties/index.html
Mass property functionality for rigid bodies and colliders.
Mass is resistance to linear acceleration, AngularInertia (or "moment of inertia") is resistance to angular acceleration
Rigid bodies use ComputedMass and ComputedAngularInertia for the actual values used by physics. By default, they are computed automatically based on colliders, but can be overridden with Mass and AngularInertia
If you specify Mass, it also scales the automatically computed angular inertia, unless you explicitly set it with AngularInertia
how do I make this function not so horrible 
pub fn contact_manifolds(
collider1: &Collider,
position1: impl Into<Position>,
rotation1: impl Into<Rotation>,
local_com1: impl Into<CenterOfMass>,
collider_transform1: &ColliderTransform,
collider2: &Collider,
position2: impl Into<Position>,
rotation2: impl Into<Rotation>,
local_com2: impl Into<CenterOfMass>,
collider_transform2: &ColliderTransform,
prediction_distance: Scalar,
manifolds: &mut Vec<ContactManifold>,
) {
I could maybe have it be something like
pub fn contact_manifolds(
collider1: &Collider,
position1: impl Into<Position>,
rotation1: impl Into<Rotation>,
collider2: &Collider,
position2: impl Into<Position>,
rotation2: impl Into<Rotation>,
prediction_distance: Scalar,
manifolds: &mut Vec<ContactManifold>,
manifold_builder: Fn(impl Iterator<Item = LocalContactPoint>, &mut ContactManifold)
) {
so then the caller (NarrowPhase::update_contacts in this case) can handle all the transformations and stuff to build the manifold from the given points
but it's kinda weird
Maybe pack collider/position/rotation into some struct?
this might not be that avian-specific but I'm kinda curious because most of my transform changes are occurring due to avian
how many runs within a frame should I expect there to be of propagate_parent_transforms? (this is all within one update frame and the flags show where avian physics schedule starts and stops)
So this stuff is kinda messy but
- once before physics to make sure child collider positions are up-to-date
- twice right after physics
- right before transform-to-position sync to account for user
Transformchanges that happened during the physics step - right after position-to-transform sync to make sure child transforms are up-to-date
However I think we should axe the transform-to-position sync that happens right after physics and just disallow usingTransformin thePhysicsSchedulein favor ofPositionandRotation. And also remove all of the propagation that we do after physics since it shouldn't affect actual physics behavior, and Bevy runs its own propagation later anyway, so visually it should look correct
- right before transform-to-position sync to account for user
This would cut the number of propagation passes from 4 to 2, once right before physics and once at the end of the frame with Bevy's own propagation
Iβve had something like that in mind before and I agree.
FYI, the propagations also had a fairly big footprint on Chainboom when I ran tracy on it recently
Less then the GPU stuff though π
Actually I think we could/should also axe the propagation before physics since it's only useful if people manually modify transforms of parent rigid bodies, which is not recommended anyway
yyah who would do that π
So then Avian wouldn't do any propagation, it'd be all Bevy
yeah, I'm trying to squeeze out performance where I can. Looking at chainboom for reference at times
(or modifying transforms to teleport is fine but for continuous movement it's not ideal)
yeah, I have a pretty smooth teleport thing working at the moment but it's like one-off teleports, not continuously doing it
Lemme try this, it'd remove like half of the SyncPlugin π
I'm sorry I nerd sniped you into doing something crazy
nah this is good
I might finally remove PreparePlugin and rename SyncPlugin to PhysicsTransformPlugin
mm also we need to rename our system sets from FooSet to follow the FooSystems convention (that I myself added for Bevy lol)
Didnβt even consider that, makes sense
Excited for that π
I'd be surprised if the answer to my question is actually "oh, we can just remove all that"
"oh these systems are taking half of your frame time? k remove them then"
"it's actually faster if you remove the avian plugin π€ "
"why not turn off your computer?"
If your physics are so deterministic, why donβt you just precompute all frames of the game??
Checkmate Avian fans
it appears to work, with the caveat that sleeping is broken
I'm guessing it breaks the current hack of using PreviousGlobalTransform to avoid unnecessarily changing transforms every frame
wait
hah no it did not break sleeping
it's already broken on main π
yeah it's just the force stuff waking bodies up when it shouldn't... I wonder what we actually want to do with sleeping and forces
Specifically, should applying a force or impulse wake up a sleeping body?
Historically for Avian it has, because I wanted things to "just work" and that sleeping is purely an internal optimization
what's the argument for why it wouldn't?
However, overwhelmingly the default is actually no in other physics engines (Rapier, Box2D, Jolt) and you need to explicitly specify that you want to wake up the body
In general you want to avoid any cases where you unnecessarily wake up bodies, like let's say you have custom planet gravity via forces and a body is resting on the surface of a planet; it should be sleeping
But I do think we probably want to default to waking up bodies
I can see how it'd be desirable to control that. That does seem kinda annoying at the same time though
Similarly, a lot of engines don't automatically wake up bodies when gravity changes, or when you remove a surface from under a body
I think I'm still of the opinion that sleeping should be fully transparent to the user by default, but there are ways to avoid waking up bodies when you need it
I think impulses should wake up bodies, and forces should not
Imo it should be configurable for both, but the defaults should be consistent between different types of forces
So wake up by default for both but have a way to change it
that makes sense to me
Rapier has a wake_up boolean argument in all of its force APIs
rigid_body.add_force(force, wake_up);
Box2D has the same
I'm not sure if I like having it in all the APIs like that though
or bevy_rapier has no way to configure this, it just never wakes up bodies when applying forces
that's a tough choice
I think we could do something like this
forces.apply_force(force).wake_up(false);
where it defaults to true
but it does make the default more implicit, for better and for worse
and for the constant force components it'd be awkward, but I think for those it's fine if we don't wake up since they're "constant"
Oh Unity and Godot also default to waking up
By default the Rigidbody's state is set to awake once a force is applied
https://docs.unity3d.com/ScriptReference/Rigidbody.AddForce.html
the body will not move and will not calculate forces until woken up by another body through, for example, a collision, or by using the apply_impulse() or apply_force() methods.
https://docs.godotengine.org/en/stable/classes/class_rigidbody3d.html#class-rigidbody3d-method-apply-force
It's interesting since physics engines tend to expose more control here and prefer the solution that is best for performance, but game engines focus on providing user-friendly defaults for their APIs that wrap those physics engines
And Avian is kind of in the middle where it's both the implementation and end-user API
I think I'll try to get this working π€
Or maybe call it no_wake_up() to better signal that the default is to wake up
wait, would you have a way for the user to set the default (wake up vs not wake up)?
not easily, but probably somehow
I'm not sure we want it to be globally configurable though
I guess if it's not globally configurable then it doesn't make sense to have a boolean parameter, right?
yeah
mm this sort of API does feel kinda weird though because of cases like this
forces.apply_force(force).no_wake_up();
forces.apply_torque(torque); // should this wake up?
that second line should wake it up, right?
yeah I think so, but we need methods like apply_force to return a sort of builder to make it work like that
and wake up / not wake up on Drop
it'd be really nice if we had argument defaults... π
pub fn apply_force(&mut self, force: Vector, wake_up: bool = true) { ... }
Or even this
pub fn apply_force<const WAKE_UP: bool = true>(&mut self, force: Vector) { ... }
Hmm or we actually already don't allow chaining for the force API right now, so we could make it like this
forces.apply_force(force); // this wakes up
forces.disable_waking();
forces.apply_force(force); // this would not have woken up
that seems fine
and then on Drop it is reset
that seems reasonable
I don't think I can store local data on QueryData so naturally I'm making a separate private component with bitflags just to control this waking behavior :P
bitflags::bitflags! {
impl ForceFlags: u8 {
/// If set, applying forces, impulses, or acceleration will wake the rigid body.
const WAKING_ENABLED = 1 << 0;
/// If set, the rigid body is queued for waking by a force, impulse, or acceleration.
const WAKING_QUEUED = 1 << 1;
}
}
Can we get some more reviews for this highly important PR?
https://github.com/bevyengine/bevy/pull/20190
(sharing the message, see https://discordapp.com/channels/691052431525675048/831640344668012555/1395849830961647616 and #off-topic)
I fear Alice is going to point to this as evidence that we have plenty of people who actually would review but don't or something
Is there a default unit of measure for the engine, I'd assume it would be 1 unit = 1 meter.
(I did approve)
and if there is, is it configurable somehow?
A plugin group containing all of Avianβs plugins.
thanks β€οΈ
yeah so it's in fact meters by default
nice, Imma test scaling it down a bit
as I want to simulate a pachinko/pinball kind of thing
hmm
hmm
I set it up to 1000:1 and rigidbodies are freezing π€
maximum sleep velocity is scaled
yeah I assumed that would be involved
but I thought this was one of the things that the WorldScale parameter is for

it does say "It is recommended to configure the length unit to match the approximate length of the average dynamic object in the world to get the best simulation results."
so i think instead of using it as a way to get objects to feel larger/smaller you should scale it to the world-size of your pachinko ball and then just tweak the other numbers accordingly?
it is, just in the opposite direction
Oh I thought of a pretty nice way to handle the force waking thing, we can just have a ForceWaking component
#[derive(Component, Default)]
pub enum ForceWaking {
#[default]
Wake,
NoWake,
}
This can be a required component for rigid bodies, and then Forces would just have a way to set it
// Prevent forces from waking up the body
// until the waking mode is set to `Wake`.
forces.set_waking(ForceWaking::NoWake);
This makes the default documented via required components, and also allows users to set it per-entity if desired
I'm trying to think of scenarios where that would be less friendly like.. if you had a force that wanted to apply to everything regardless like an explosion but you don't want to alter all the components π€
Unsolicitated suggestion: NoWake variant might be renamed KeepAsleep or something more descriptive.
Or you know... a boolean tuple struct struct ForceWaking(boolean) π
I renamed it to ForceWakingMode since ForceWaking sounds like "this forces bodies to wake up" rather than "this configures whether forces wake up bodies"
In Jolt there is a similar EActivation enum with Activate and DontActivate variants, which is passed as an argument to methods like AddForce, defaulting to Activate
(I don't like DontActivate because it's "don't", not "dont", I'm nitpicky like that)
Just typo the Wake a bit and then the other variant shows up by itself
Woke and Broke 
I made this an enum because I like that when you're calling set_waking_mode, you see the ForceWakingMode component in the method call instead of a random bool. This is self-documenting in the sense that it makes it clearer that it is directly connected to that component, and it is not some independent waking configuration that is somehow local to Forces in that system
Actually Wake or Break could work, because if you don't Wake the entity, it will Break the expected behavior of your forces π€£
But does setting the waking mode actually work? If system A sets it to no wake, then B applies forces and does expect it to just wake but doesn't set it since it's the implicit default behavior, will it wake or not?
It will not since you've set the force mode to not wake
But you could reset it after applying the force if you wanted to
but yeah that is probably the main downside with this approach
Youtube react content about how avian is the first woke physics engine
I mean it's written for bevy, which has probably the highest % of LGBTQ people of any game engine community, so objectively it is the wokest physics engine
Even if Jondolf is afaik not part of the LGBTQ+ community yet
Hack commedian: "Avian is so woke, when Jondolf implemented the pronouns component, it just wrapped a string!" Audience proceeds to burn down the venue
Would be great if it was just per-call, but then we'd need the optional argument thing (not possible, or maybe scuffed with const generics), or seperate functions for waking and non-waking forces
Sorry I'm done
Really the ideal approach would be default argument values
fn apply_force(&mut self, force: Vector, wake_up: bool = true) { ... }
but we can't have that because of Rust. And I have not figured out a nice way to make the chaining approach work in any logical and intuitive way, and it has a lot of weird cases like this
// does the first or second force here wake? or neither?
forces.apply_force(force).no_wake().apply_force(force);
// does the previous no_wake still apply here or not?
forces.apply_force(force);
Can we do like
forces.non_waking().apply_force(force)?
Everything from .non_waking() doesn't wake, until you drop the retrun value from it and use force directly again
(methods don't support const generic defaults, I tried)
Ofc these cases shouldn't actually happen, since you either want all forces to be waking, or none of them, or the waking force is conditioninal (and thus in a seperate codeblock, or sometimes zero)
I guess that's also worth considering: A waking force of zero shouldn't wake
yeah but I think it's reasonable to think that forces.apply_force(force).non_waking() wouldn't wake either
yeah I have that
Can't you just #[must_use] on non_waking()?
Alternatively, don't allow chaining
mm we can probably do forces.non_waking().apply_force(force) and disallow chaining for the actual force methods
(we already don't allow chaining atm)
the question is just how we do it cleanly and efficiently, we might need non_waking to return a wrapper type or something
aaaaaa
why is the field of RigidBodyColliders private??
That's pretty much what I would expect yea
i should be able to access all child colliders of a parent rigidbody....
Might even have both implement a trait so they have identical methods and could be passed to APIs in a way that's waking agnostic
Mm yeah I'll make a PR to implement Deref and some iterator traits for it
We can't allow direct mutable access to the field since it would let you break the relationship if you added or removed colliders through it
Children is the same way
but we can add read-only access
honestly i feel like the simplest solution for the waking thing is forces.apply_force(_); forces.wake();, but that leaves the default off
there could probably be a Forces::reborrow, so making a wrapper struct for non-waking forces could mean also implementing QueryData for it & letting the fns to swap between them consume
I did get non_waking working now
forces.non_waking().apply_force(force);
There's a ForcesItem which is what the Forces QueryData returns, and a wrapper NonWakingForcesItem which is returned by ForcesItem::non_waking
The two types of force items share a RigidBodyForces trait, and an associated WAKE_UP constant is used to determine whether it should wake up bodies or not
forces.wake() would be kinda pointless since you can just commands.queue(WakeUpBody(entity)), and I think it's quite important to have the default to be to wake up here
It's some internal boilerplate but I think I like this
i recently found out about the Rotation component, whats the benefit over just using transform.rotation?
It's a global rotation used internally for physics. Bevy's GlobalTransform is mostly read-only and stores an Affine3A, while physics needs a mutable global Position (Vec2 in 2D, Vec3 in 3D) and global Rotation (Rot2 in 2D, Quat in 3D) and also optional f64 support
For user code you should typically just use Transform
did this PR too now
https://github.com/Jondolf/avian/pull/784
(FYI @rough nebula)
How would you guys go about having stacked structures be more stable? I'm building an angry-birds-esque type of game and I'm mainly doing towers of bricks like in Jenga.
I know simulating all the time and expecting physics to be stable and not crumble is not acceptable due to it being... well... the main bottleneck of physic engines...
Also I think you can already access the entities via the collection method or iter, since it is a RelationshipTarget and they all have those methods
but my PR provides some easier access
so I'm okay with workarounds, that's mainly what I'm asking for
is there a way I can make a group of colliders "disabled" until an strong force is applied?
so that I could start the world with the entities in place but "frozen" (as I know the should be stable) and as soon as a "big hit" happens they unfreeze and start simulating again
Because this "isn't working" and I know it won't get anywhere near perfect, given that I'm probably going to stack way more rigidbodies than these
You can increase the SubstepCount, though that amount of jittering seems a bit abnormal with such a small stack. Once they're mostly settled, sleeping would deactivate them and remove any remaining movement, but it currently doesn't work for piles or stacks of dynamic bodies because we don't have simulation islands yet
What colliders are those?
Cuboids would probably have the best results
Yeah, already tried increasing substeps but the framerate tanks way too much and it doesn't really get that much better
These are unfortunately generated trimesh colliders
from the bevy_trenchbroom plugin
didn't know that would have an effect on simulation accuracy
I think you should be able to set a different generated shape for it, lemme check how we handled it in Chainboom
are simulation islands coming any time soon? because I would probably greatly benefit from it if it puts them to sleep
in chainboom?
hahah
a jam game?
gotta check it out
Yeah we had this sort of thing
https://github.com/Bevy-Jam-6/chainboom/blob/main/src/props/generic.rs
The setup module has the collider setup stuff
Which can use any ColliderConstructor
Hopefully yes, we'll see if I have time to add them for 0.4. There's a lot of other things I still need/want to do for 0.4 as well
I watched that thread pool talk. It's interesting that he gets so much power out of spinning. In general, spinning instead of sleeping performs worse on most of my benchmarks. It might be because the tasks are so truly tiny.
I'd love to get graph coloring set up as a benchmark.
Does avian have an implementation of that?
It's probably because spinning puts the lock under contention for me. He uses lockless stuff, but that can still be contested, especally in a really hot loop.
but it uses mainly ConvexDecomposition and ConvexHull, how do those defer from what I'm already doing?
Yeah so we do have graph coloring, but it is done incrementally with a greedy algorithm whenever constraints are added or removed, rather than running some global graph coloring algorithm as its own step. It's the same approach described in Erin's SIMD Matters article
https://box2d.org/posts/2024/08/simd-matters/
SIMD in game development Often in game development we talk about SIMD. It is the holy grail of CPU performance, often out of reach. The conventional wisdom is that it is difficult to achieve real gains from SIMD.
It is tempting to build a math library around SIMD hoping to get some performance gains. However, it often has no proven benefit. It j...
The implementation can be found here
https://github.com/Jondolf/avian/blob/main/src/dynamics/solver/constraint_graph.rs
I thought about checking the mesh I'm importing from trenchbroom, and if it has 8 points and they're a cuboid I just generate a cuboid from the points
I'm changing some of the details soon for wide SIMD and to do some tunneling prevention improvements, but it'll mostly stay the same
Once you have the colorings, it should just basically be the repeated application of par_iter on the color-lists for each sweep. So maybe the thing to do is just write a hyper-optimized par_iter impl for forte.
I might have a go at writing a graph-coloring step as well, we'll see.
Triangle meshes are hollow from the inside and generally one of the worst shapes for dynamic bodies, convex hulls are solid shapes but won't account for concavity (which is fine for your case), convex decompositions are basically many convex polyhedra used to approximate even concave shapes
Ideally you would construct an actual cuboid collider if you can get its dimensions, but a convex hull is second best
fuck I gaslighted you, I missremembered from trying out bevy_skein past week
It's actually a convexhull
so I think Im gotta have to go for cuboids
it shouldn't be neither too hard to do nor too expensive, so lets see
given that mostly my shapes will be in fact cuboids
Mm that still feels weirdly jittery if it is a convex hull, but I guess I have seen them sometimes have some stability issues, not entirely sure if it's the fault of Parry (the collision detection lib) or Avian
Yeah, I'm hoping to look at traces for Avian tomorrow and try running the solver with forte and rayon as well, so I'll share any results I find there
and the transform propagation? π
yeah I'm doing that now
oh snap
since I got the sleeping thing fixed
Sweet. This is the exact target use case pretty much, so if itβs slower than rayon I will have to rethink/redesign things probably.
this is the motivation I need to update to the new forces
I am super curious how that'll affect benchmarks
Btw for forte, should I try running it with rayon's parallel iterators? IIRC you had a scuffed integration somewhere
or Griffin's par_map and par_chunks_mut from pool_racing
or something else?
they're pretty tiny convex hulls if that's of any help
I'm using a unit length of 0.2
and they're 0.2x0.2x0.4 in length
Oh that could be another reason it's unstable, I think we currently have some problems with small shapes
though those aren't too small
I wish I got my sleeping thing fixed (sleep deprived gang)
the reason they're small is I'm trying to replicate the feel of a pachinko/pinball machine
I wanted to make them way smaller
buuut... they were even worse
Both have flaws. I need to write a mini version of par_iter. I would try both. Not yet sure what the trade offs are.
at 0.002 units of length π
@vestal minnow funny story jon, i made shotgun bullets and I decided to be a smartied and made them have rotation onto themselfes u know like that movie , turns oujt i made my bullets make too many barrel rolls, which made them go straight into the floor anyways this is my podcast thank you for listening
the diff is looking kinda ridiculous so far lol
I'm just
ing so much stuff
and it seems to be working like normal still π
lol I do think of code sometimes like scaffolding and over time it's not unusual for some of it to no longer be "load bearing"
I mean
The custom cube colliders are in fact generating correctly hahaha
got it guys hah
just gotta find why the transform is fucked
and pray it solves some of my problems
seems like things might not be parented correctly?
nah, I think I just totally forgot about the transform
it is what you get when you're retarded
everything under control π
Draft PR here, I'll finish it tomorrow
https://github.com/Jondolf/avian/pull/785
TLDR:
- Yeeted like most of the
SyncPlugin, and renamed it toPhysicsTransformPlugin - Yeeted the entire
PreparePlugin - Cut down from a total of 4 propagation passes to 2 (including Bevy's), with an option to also disable the one before physics
- Shuffled some modules around
- Renamed
PhysicsSet::SynctoPhysicsSet::Writeback - Added some new convenience system sets like
PhysicsSet::FirstandPhysicsSet::Last, because why not - Probably something else idk look at the diff
(cc @visual sparrow if you're interested)
Changelog: look at the diff
the best changelog
I almost didn't ask that question about the propagations lol
This cuts the total number of transform propagation passes from 4 to 2 (including Bevy's own). I kept the one before physics since it can still be important if you do modify transforms before physics, and bevy_rapier also has it. However I made this configurable via PhysicsTransformConfig::propagate_before_physics, so you can remove that too if you want
...except I didn't add the actual run condition π lemme add that real quick
got it working @vestal minnow although it didn't seem to make a difference π₯²
anyway I think this could be a good contribution to bevy_trenchbroom, so the effort wasn't in vane
maybe I'm doing something wrong? you said this amount of jitter isn't common, so now I'm not sure
π₯² making gravity lower solves most of the jitter problems, but it isn't something I can afford
iirc really small values can contribute to this kind of thing. I know you're trying to simulate real world physics of small items but idk, I'd try scaling up and seeing how that affects it
yeah, I tried scaling it up but had no luck
If I scale it up and scale the gravity up with it it only gets ever-so-slightly better
This is the system scaled x10, but the gravity stays at x1
so the jitter is less noticeable just because there's 10 times less force being applied to the boxes
best I have for now is to just have all colliders be asleep until something interacts
not ideal but will do at least while I prototype
they're less jittery if you offset every other row
what do you mean by offset?
like in a brick-wall pattern?
they're already offset if that's what you mean π₯²
hard to see, i suppose
Woah thatβs huge π thanks!
Merging the physics transform cleanup PR, it seems to work based on what I've tested
do let me know if it breaks things though
(edit: CI is still failing π merging once that's fixed)
does it have an impact on those crazy performance metrics you do?
haven't tested, probably nothing significant since they don't even have any hierarchies
(also I fixed the remaining CI problems so it's merged now)
I'm trying to run Tracy now to profile the solver, but I'm getting some weeird traces
This is one iteration of the solver
It's definitely multi-threaded since it's way slower if I disable parallel, but the trace doesn't display it properly, or something else is horribly wrong
hmm I'm gonna update and check it out
Iβll port Chainboom to it later
Does Tracy not show intra-system parallelism for Bevy? It's showing the spans on the main thread but the parallel version is faster
same when running with bevy/trace_chrome
do you mean like what I had here?
I haven't used tracy before, I don't think they have pre-compiled linux binaries
The solver's systems like solve_contacts are parallelized but they are shown only on the main thread
I'm on Arch and installed the tracy package through AUR, which seems to work but idk if traces looking like this is a me-problem or some problem with that
hmm, the force changes took a lot less time to update than I thought
ah, lol, forgot Forces has LinearVelocity/AngularVelocity in it
Is it normal that intra-system parallelism is shown like this with unlabeled par_for_each spans
Yeah it has getters for them if you need them and have some other conflicting query
yeah
yes
unfortunately
forces update complete π
||it actually does work lol, I need to adjust their spawn heights or something||
Some distros have, but I had to compile my own. Fortunately itβs not that hard compared to some other C++ projects π
Working perfectly as intended 
Mm I think par_chunk_map_mut doesn't add spans which is why it's not showing up in Tracy
(we currently use that for parallel iteration since Bevy doesn't have par_iter over slices π)
I want something closer to this
meanwhile ours looks like this lol
bevy tasks but it doesn't seem to output spans unless you're specifically using the parallel iteration methods on Query, which is pretty rare for us
bevy tasks is incredibly bad lmao
most parallel iteration is over slices/vectors
does rayon output spans properly? I didn't see any tracing features on it or find anything related
or do you need to manually add spans somehow
rayon dosn't have spans.
forte kinda has tracing info. I mangled it a bit during the optimization process.
If you'd like I can add them back.
Is there an idiomatic way to handle collisions through Portal-style portals? Firstly to somehow disable collisions with the wall the portal is on, but only where the portal is, but then second to somehow detect collisions between objects that are far apart but still touching through a portal. Some kind of collision proxy or something?
Yeah would be nice behind some trace feature flag
I'm not sure how I'm meant to look at what kind of gaps I have between the different tasks when I can't even see the tasks properly π€
This is why I use dtrace (and instruments on macos).
for rayon specifically
You can get the information about what threads are scheduled on which cores during which time-slices directly from the OS.
Also I'm trying rayon now but it's literally doing nothing faster even though it should have 24 threads and I tried to set the ComputeTaskPool's threads to 1 
Like it's basically as fast as single-threaded
Not being slower is a good start.
With bevy tasks I have roughly 3x perf over single-threaded but with rayon there's barely any improvement, something has to be wrong
That's odd. Rayon is usually pretty good about setting stuff up automatically.
But it's also fairly high overhead.
It's possible your workload is latency-sensitive?
Could be
It doesn't look latency sensitive
Rayon should be doing it's thing at timescales of a few ms
oh, are you using briged iterators or parallel ierators?
Just par_iter_mut
odd.
that should be working.
you should maybe just disable multithreading for bevy, go fully single-threaded.
could be thread contention.
I've tried that too
and rayon is doing nothing
can you push your benchmark today? I can profile it and figure out if rayon is going wide or not.
Later tonight.
Quick question, I have a dynamic rigid body as character controller and want to attach a child sensor collider that will move with the parent rigid body. but when i apply impulses to the parent the child does not move it stays in the same global position, but if i edit the transform of the parent the child body moves. How can i make the child collider always relative the parent?
(also, fwiw, 3x perf is not great by itself. How many cores do you have)
Yeah 3x is bad, I have 16 cores and 24 total threads
https://www.intel.com/content/www/us/en/products/sku/230491/intel-core-i713700f-processor-30m-cache-up-to-5-20-ghz/specifications.html
oof
Okay so it is doing something but it only really helps the narrow phase (which is fully parallelizable), this is with rayon
this is without
also rayon tanks Warm Starting lol
(this is single-threaded)
what do the less parallelizable phases look like? how many calls to par_iter ect are you doing?
With bevy tasks, for reference
I doubt rayon-forte-compat will make too much of a difference until I publish my current set of changes, but I'd be interested to see it.
At every tick, six iterations of 4 systems that do basically this
for color in colors {
color.constraints.par_iter_mut().for_each(|constraint| { ... });
}
where colors has 12 items
I should still change it to ignore colors with no constraints though, and to move constraints to a serial overflow set if there are very few constraints in a color, like under 32 or something
so this is the precomputed color thing?
how large is each list of constraints typically
In these images, "Colors" displays how many constraints are in each color
The number of constraints just depends on how many collisions you have, typically anywhere from 2 to 8 colors are occupied with a large number of constraints for heavier scenes, and they tend to be spread out fairly evenly
Ok, so it's about 200 ns per item, more or less? (5.8 ms / 12 / 2500). I can see how rayon's latency would be significant in those profiles.
At least in these sorts of benchmark scenes
200 ns per item is long enough to make bevy_tasks performant, and when it works it does have comparatively low overhead.
This is just a large 2D pyramid
Wait, and you do six iterations? Do the 4 systems run in parallel or sequentally?
Oh yeah, I can totally see how rayon wouldn't work here. That's super interesting.
(as a case-study, for me, that's great news)
I think... I am seeing something weird where my angular velocity randomly jumps up by a lot
An outline of the simulation step looks roughly like this
broad_phase();
narrow_phase(); // par-for-each over contact pairs
// we do this incrementally as part of the narrow phase,
// not as its own step
graph_coloring();
prepare_constraints();
for _ in 0..substeps {
integrate_velocities(); // par-for-each over bodies
warm_start(); // uses graph coloring
solve_constraints_with_bias(); // uses graph coloring
integrate_positions(); // par-for-each over bodies
solve_constraints_without_bias(); // uses graph coloring
}
apply_restitution(); // uses graph coloring
store_contact_impulses();
By "iterations" I really meant substeps
The parts I've marked with "uses graph coloring" iterate over the colors serially and par-for-each over constraints in a given color
This is the general simulation structure used by most engines, excluding details like whether restitution is added directly to contact impulses or applied as a post-solve phase like we do, and whether graph coloring or just inter-island parallelism is used
This is pretty much exactly the simulation structure used by Box2D though, which uses graph coloring and wide SIMD
And yes each of these steps must run sequentially one after each other
The way to think about this is:
bevy_taskshas a low per-call cost, and a high per-item cost. the costs amortize primarily as the amount of the work done per item increases.rayonhas a higher per-call cost (caused by aggressive stealing and work starvation) but low per-item cost. the costs amortize primarily as the number of items being operated on increases.fortehas low per-call and per-item costs, but has lower max-throughput throughput thanrayon. In the current form, it's especially sensitive to the amount of work per item.
Here, the 6 iterations and 6 filled colors means 36 distinct calls. so the per-call overhead shows up strongly. The longer each item takes, the better I'd expect bevy_tasks to perform. The more items in each color (imagine you had a million in each, not a few thousand) the better I'd expect rayon to do.
Is it possible to match the mask layer with a specific physics layer in some way?
LayerMask should implement From<L> where L is a PhysicsLayer like your GameLayer, so maybe you can do GameLayer::Human.into() here?
nvm we don't want to check equality here, we want to check if the given layer is contained in the layer mask
Or explicitly convert it to a LayerMask if it can't infer the correct type
I'm not 100% sure if it works like that with matching, I haven't tried
Expressions are not allowed on patterns
So I have to make crutches
I think you can also do layers.memberships.has_all(GameLayer::Player) actually
the name is a bit weird, maybe contains would be better
I'm trying to get forte working but it's refusing to find rayon-compat
[patch.crates-io]
rayon-core = { git = "https://github.com/NthTensor/Forte", package = "rayon-compat" }
error: failed to resolve patches for `https://github.com/rust-lang/crates.io-index`
Caused by:
patch for `rayon-compat` in `https://github.com/rust-lang/crates.io-index` failed to resolve
Caused by:
The patch location `https://github.com/NthTensor/Forte` does not appear to contain any packages matching the name `rayon-compat`.
it's called forte-rayon-compat technically
there's also a version on cargo you can use.
I think I need to update the readme
things are still slightly messy and unstable.
also, I'd hold judgement until I can publish alpha-5. Work-stealing and idling has been significantly improved.
Suggestion: new channel #physics-dev to help avoid burying questions. This channel is probably the all-time highest traffic #1034543904478998539 channel, partially because of how much dev discussion goes on. We have the #ecs /#ecs-dev channel divide for exactly that reason. It's not first-party bevy development, but given the community-perceived high likelihood of upstreaming this crate, I think you could probably make a really good argument for the exception. Food for thought π
I've wanted this in the past but yeah we could always ask again
We also have over a third of all the messages of all ecosystem crate channels combined :P
And often even more activity than #math-and-physics
If you want to split the parallelism discussion into it's own thread, we can do that too.
Idt you can make a thread in posts unfortunately
I think in this context it's directly related to Avian and it's more visible to discuss here than make a separate thread somewhere in #math-and-physics
If there were an avian dev channel a thread might make sense though
But yeah it would be nice to have a split for "avian development" and "avian help and questions"
People have asked me to make a separate Discord server for Avian in the past, but I would really like to not split away from the core community Discord just because we don't have a dedicated channel for Avian development
Agreed. That'd be a painful divide
Also more broadly I think it would be good to have a place to discuss physics development, since it would also be a place where people could freely discuss character controllers, collision detection, BVHs (for collisions or spatial queries), etc.
Imo these don't really fit that well under #math-dev
Right now I would discuss those sorts of things here in #1124043933886976171 because they're still somewhat related to Avian and I don't think there is really a better existing dev-oriented channel
But it doesn't feel like it necessarily should be the place for that :P
I don't mean to change the topic so feel free to respond to this when this topic is done!
I am trying to implement the idea of using transform interpolation for smooth and continuous rendering in Update schedule when the physics is in fixed step (such as in this example: https://github.com/bevyengine/bevy/blob/main/examples/movement/physics_in_fixed_timestep.rs) This example does its own physics, but I am trying to instead do this with Avian. I am a little confused right now because I currently understand that avian does physics on the "official" Transform component but the concepts from the example suggests I should have physics operating on its own decoupled component (in the example it is a current and previous translation) and then I would use those to interpolate it into the entity's "official" Transform component. I would appreciate any guidance!
Imo it's done enough for me to forward the convo to Alice and beg
Avian has built-in transform interpolation support via my crate bevy_transform_interpolation π
See the Avian docs here
https://docs.rs/avian3d/latest/avian3d/interpolation/struct.PhysicsInterpolationPlugin.html
A plugin for Transform interpolation and extrapolation for rigid bodies.
TLDR, just add TransformInterpolation to an entity and it should get interpolated
Yeah I'll probably ask about it again in #community later today or tomorrow, feel free to remind me if I forget
Thank you, how easy!! π
Hey I wrote that π As Joona says, the interpolation is done for you, can just use Transform.
Ping me if you have any other questions for how that example relates to Avian
Sounds good, I'll keep my eye out!
I also didnβt have a good place to discuss developing Avian Pickup
This channel is of course relevant, but not quite on topic for "How does HL2 implement pickup and how do I translate that to Bevy?"
And itβs wayy too niche for an ecosystem crate channel
Oh, is your (excellent) crate based on hl2??
Also not quite sure where development of my navmesh stuff I want to eventually upstream should live
Very heavily! Used about 3 sources of semi-legal source code as inspiration hehe
I think in the past I've also asked if it'd be a good idea to have an #1034543904478998539-dev channel with topics for crates that want their own dev channel, but it might get confusing so idk if I like that, I think a #physics-dev would still be better for what I would want
Yeah I also want that 
And yeah this is another good example to mention
I think navmesh can also somewhat fit in there
#math-dev feels too abstract imo
I currently just use #1364848195670114325 to discuss development, which is definitely not the correct channel lol
Everything's just math at its core. #rendering-dev ? #math-dev . #ui-dev ? #math-dev . #community , #moderation , #welcome ?
#math-dev
Yeah I wouldn't want the channel to be just #avian-dev in disguise (even though it'd be that too :P), it'd be good for navmeshes, collision detection, character controllers, a potential VHACD port from Parry into a standalone Glam crate, or any other important physics development discussions
and you can create threads as needed, which you can't do here
Also for bugging nth into helping with parallelism in physics stuff
I also need that for the navmesh
Imo this is the important part exactly
(brb gone for 10-15 min)
ok, on the new forces stuff.. I'm having a weird thing (basically that video clip I posted before) and am having trouble tracking down what it could be...
I thought it was that the cars were spawning slightly in the ground but that doesn't seem to be the case. It's like.. somehow in the first couple frames of them being constructed, a large force gets applied to them. But, if I have physics paused while they spawn and then unpause everything is fine
not sure if there's something weird like.. if I have an entity with LinearVelocity that gets a rigid body and center of mass added later rather than all together and if that causes some weirdness?
You're probably using apply_force_at_point?
I think I'm using apply_linear_impulse_at_point everywhere now
I'm trying something, I think... it might be sorta what I said
I'm guessing the GlobalCenterOfMass hasn't yet been computed before the first run of physics, so then there's a crazy torque for bodies that is larger the farther the bodies are from the origin
Also note that the point is a point in the world unlike in 0.3 where it was relative to the given center of mass
Is there a way to tell when it's been computed?
I think what ended up happening was I added enough logging and gizmos that slowed the game down that it started behaving correctly lol. It is like... some kind of timing thing.
You could log it right before you apply the first impulse
If it's at the world origin but the body isn't then it's wrong
I did notice that.. this happens for all my vehicles except one whose center of mass I have configured to be Vec3::ZERO
let me try that
@vestal minnow btw did you happen to implement the thing we discussed regarding collision strength for deciding whether to break glass?
yeah, looks like the first frame it's still world origin
Vec3(-684.1957, -24.869493, 668.8615) GlobalCenterOfMass(Vec3(0.0, 0.0, 0.0))
also, I think GlobalCenterOfMass is private. Which is fine. I'm unsure though if I should add something to check for this in my code
This should be easy to fix on Avian's side, I think I have an observer to compute mass properties on spawn but forgot to update GlobalCenterOfMass there
I can try to repro and make a PR later
Not yet
IIRC there was some 2D physics engine that had some cool fracturing example, could be fun to replicate that and use the collision force or impulse to determine when it should break
This one from Chipmunk2D, with some sort of Worley noise approach for shattering shapes
https://github.com/slembcke/Chipmunk2D/blob/master/demo/Shatter.c
I haven't run that though so I don't know what it looks like :P but the implementation is quite short and simple
though I think most shattering algorithms I've seen use some Voronoi approach
oh wait this is a Voronoi approach too I think
right Worley noise is an extension of the Voronoi diagram
ah, I think I see it. Looks like it's using a systemparam for computing the other mass properties..
skipping applying impulses until globalcenterofmass isn't equal to zero fixes the issue for me at least π
similar thing happens when I teleport, gonna look into that now
ffs has anyone ever managed to correctly patch dependencies in Avian's workspace? I put the patch in the crate's Cargo.toml, it tells me that it must be specified at the workspace root, I put it at the workspace root, it tells me it's ignored because it was not found in the crate graph
to this day I think the only way I've managed to patch deps for Avian is to fork every dependency that uses the crate I'm patching but I would really prefer not to do that with rayon
with other crates I have gotten patches working, just not with Avian
I patch bevy and have all the crates forked
I think it depends on where your deps are defined, but also avian's structure is a bit scuffed isn't it? π
I'm trying to do this
# in workspace root
[patch.crates-io]
rayon = { path = "../rayon" }
rayon-core = { path = "../Forte/rayon-compat", package = "forte-rayon-compat" }
Yep! Basically just the inversion π
or only patching rayon-core, but neither work :/
warning: Patch `rayon-core v1.12.0-dev (/home/joona/Programming/Forte/rayon-compat)` was not used in the crate graph.
Check that the patched package version and available features are compatible
with the dependency requirements. If the patch has a different version from
what is locked in the Cargo.lock file, run `cargo update` to use the new
version. This may also occur with an optional dependency that is not enabled.
I do have rayon-core as a dependency
it looks like you already have a local checkout of rayon. why don't you just go replace the rayon-core dependency there with
rayon-core = { version = "1.12.0-dev", path = "../Forte/rayon-compat", package = "forte-rayon-compat" }
I did but then it conflicts with Parry and some other crates
ah
so I'd have to fork those too since just the patch isn't working
:/
it is kind of a hack, sorry about that. hope it ends up being worth the struggle.
actually hmm I think I can just disable parallelism for Parry, I don't think I'm benefitting from it anyway here
@bold garnet Hmm I'm getting these sorts of results
The old rayon profile for comparison
not encouraging
publish this branch somewhere and I'll have a look.
There are some practical problems with the current published version, I want to compare with my local work.
yeah I'll push this and the rayon fork, sec
I suspect it's just failing to go wide, which I think is an issue I've mostly fixed.
Here, actually let me jush push my WIP stuff and you can try it.
perhaps give this a shot?
pay no attention to the // SAFETY: TODO comments.
Avian with Forte https://github.com/Jondolf/avian/tree/forte
Rayon fork https://github.com/Jondolf/rayon/tree/forte
I'm running cargo run --release --example pyramid_2d in crate root workspace root
lemme try
Looks slightly better but not great

I wonder if there's something deeper going wrong π€ it feels weird that bevy_tasks would be so much faster here
I will have to do some profiling.
Mmm that one is nasty... To have an up-to-date global center of mass after teleporting, we would need Forces to have access to the global transform, but if this is a child entity, it's not necessarily available until the next time transform propagation runs
Honestly I think we should officially enforce a rule that you cannot have dynamic rigid bodies as children of entities with Transforms
This way, the Transform is always the global transform, and we don't have to worry about any issues like this
Shit big_space uses GlobalTransform for BigSpace roots mmmmmmm
can't use local forces?
having to handle hierarchies for rigid bodies is just such a nightmare lol
could have a component that just stores the body's parent's GlobalTransform, maybe
but what if the parent of that parent moves
you need to go up the whole tree
This specific issue we could "fix" by making apply_force_at_point take an offset from the center of mass rather than a world point, but this would make a lot of gamey patterns more annoying
is there a version that uses local coords
Like you might have a raycast projectile that should apply a force at the point it hit, but it'd be a pain to have to convert this point to be relative to the center of mass of that object that was hit
Currently no because it's somewhat unclear what it should be relative to, the body origin or the center of mass
I looked at other engines and they generally don't have it either
For example Unity has AddForce and AddForceAtPosition, and AddRelativeForce but no AddRelativeForceAtRelativePosition or whatever
Huh, that would certainly change how I spawn things
Editors (Blender and TrenchBroom and I think LDtk) spawn a scene hierarchy
And the rigid body happens to be a child in that hierarchy
right the scene root has a transform too mm
In the simplest case, the scene has an identity transform
So the rigid body's transform is still the global transform
But itβs not uncommon to have a group object of e.g. 5 barrels
And then you place that group 10 times on your map
In that scenario, the barrel rigid body's transform is the offset from the group's transform
Bevy does have a command for removing the parent in place
But I would find it very unintuitive to have to do that when working with a level design software @vestal minnow
I wish Transform was an immutable component so we can run change detection hooks on it lol
sorry, I just set the globalcenterofmass to ZERO and that fixed my teleporting and I went to get some food lol
I know you donβt like this suggestion, but we could use the design where Transform is just the rendered transform and users instead always work with Position for physics objects
Though that would very controversial to upstream :p
This feels like an actually impossible problem π€
- We can't rely on
Transformin case this is a child entity - We can't immediately detect
Transformchanges to update theGlobalTransformbefore users do something that requires it - We can't recompute the
GlobalTransforminsideForcesbased on the hierarchy
Bit out of the loops, but why canβt we use the TransformHelper?
(Other than performance)
- It would be expensive to do that every time you apply a force or impulse at a point
Forcesis aQueryDataso it can only contain components
can't you argue that you can't apply forces on an entity that isn't fully initialized?
Initialization isn't the problem, that we can easily fix. The problem is just that if a dynamic body is a child entity, and you teleport it by changing its Transform, we don't know the new global transform yet, so any applied forces will be wrong for that frame since the global center of mass is outdated
We could probably detect if Transform was changed by the user and not physics, and if yes, ignore the force, but (1) that adds more cost, and (2) it'd break scenarios like y-sorting or anything else that modifies transforms, even for purely visual effects
Gameplay-wise, it could be alright to take a point that is an offset from the body origin, this is also what Godot does. It just has the mild weirdness that applying a force at (0, 0, 0) will apply a torque if the center of mass isn't aligned with the body origin, but that's probably fine
We would however still need to compute the world-space center of mass relative to the body origin, and this requires the global rotation, which would have the same problem that we don't necessarily know the global rotation. But in general this is way less problematic than the wrong position
could just store 'previous transform' and apply deltas to something
I don't think that necessarily says anything about the global transform
the parent determines what the child's local Transform is relative to
if the parent was rotated upside down, left and right would be flipped for the child
or otherwise transformed
presumably the thing that started this wouldn't be teleporting rigidbodies by just transforming the parent
besides, last time i tried moving the parent of a rigidbody on stable the rigidbody stayed in place
last time i tried moving the parent of a rigidbody on stable the rigidbody stayed in place
That's because we have a rather cursed system that updates the localTransformsuch that theGlobalTransformwill match the physicsPositionandRotation
But the Transform is still relative to the parent
Checked what other engines take as the point or offset
- Box2D: world point
- Jolt: world point
- Rapier: world point
- Unity: world point
- Bepu: world-space offset from body origin
- Godot: world-space offset from body origin
IMO we do want a world point ideally
I think we need to either
- Live with the teleport problem, but expose APIs for users to update physics positions after
Transformchanges; maybe aPhysicsTransformHelper? - Change the
_at_pointmethods to take a world-space offset from the center of mass. - Disallow moving bodies using
Transform, usePositionandRotationinstead. - Disallow dynamic bodies as children of entities with
Transform. - Implement a way to bypass
Transformpropagation for the translation and rotation of dynamic bodies, making those properties always global.
For now I would do 1
There's no shot I'm doing 3 lol
2 is nasty for actual usage
3 sounds totally fine to me :^)
and 4 seems non-viable
and 5, ehh seems sketchy and would ideally be done on Bevy's side, not hacked on to work around it and do extra work
Ask Cart if he wants first-party Bevy physics to do that, and if he says yes then I'll consider it :P
I don't think cart would want first party physics to have Position/Rotation at all, and Alice would probably agree π€£
Honestly I think this is pretty doable, we could remove them and likely not have huge losses
whether we should is another question
the biggest thing would be the lack of a 2D global transform
And weird things like skew in 3D global transforms
and no f64 support but Bevy's global transforms don't support it anyway
The whole solver only touches Position and Rotation when preparing constraints and when writing back the results of the solver
The substepped part or the actual constraint solving doesn't use them at all
Collision detection and spatial queries are the other thing but affines should be alright there (if Parry supported them properly)
@vestal minnow is a new release coming soon when you've finished this?
what's the downside with 1 exactly assuming that allows a teleport? is it just the increasing the API "footprint" aspect of it?
btw is there a good way of fixing ghost collisions without tunneling? i'm using ldtk so the hitboxes are small squares next to each other
@vestal minnow @hoary turret does that mean static colliders that are touching or intersecting never sleep?
Sleep only applies to dynamic bodies afaik
That would make sense to me
yeah only dynamic bodies sleep, but currently only if they're not touching other dynamic bodies, because we don't have simulation islands and per-body sleeping is bad
Thanks for the explanation!
Nothing really, except that you have to know to do this after teleporting in case you're seeing problems. We can document that
yeah :\
I'm going with that approach for now since it's the least intrusive and has less uncertainty than the other options IMO
No way to detect that case to print a warning, right?
At least, I canβt think of any
nope
Agreed
with change tick tracking shenanigans maybe, but I don't want to go that route lol
Yeah no π
For me personally, teleporting is danger zone anyways due to interpolation
And of course chaotic physics interactions
So at least in my case, errors in this case would not be entirely unexpected. I think itβs fine it itβs well documented and thereβs a nice API
yeah, honestly, my teleportation code is very "un-physics this thing for a sec and reset everything"
I feel like expecting the physics engine to be resilient to that is a little unreasonable
There's something so unsettlingly satisfying about just watching physics interactions, monke brain goes 
the stability of the towers might be a problem, but holy shit the destructibility would be the opposite of a problem
hahah
are you using motion blur in that?
yup
although the artifacts/ghosting don't come from motion blur, they come from TAA
or... I guess a combination of both
it looks more cool than buggy tho
yeah I like it
fn cull_travel_ports(world: &mut World) {
info!("cull");
let mut travel_lane_query = world.query::<(Entity, &TravelLane)>();
let mut lanes_to_add = Vec::new();
for (entity, lane) in travel_lane_query.iter(world) {
lanes_to_add.push((entity, lane.distance));
// c!(world.get_entity_mut(entity)).insert(Collider::cylinder(0.01, lane.distance));
}
for (entity, distance) in lanes_to_add {
world
.entity_mut(entity)
.insert((Collider::cylinder(0.01, distance), CollisionMargin(1.0)));
}
for _ in 0..10 {
world
.resource_mut::<Time<Physics>>()
.advance_by(Duration::from_secs_f64(1.0 / 120.0));
world.run_schedule(PhysicsSchedule);
}
let mut collider_entities = world.query::<(Entity, &CollidingEntities)>();
for (entity, colliding) in collider_entities.iter(world) {
info!("{entity:?}, {colliding:?}");
}
}
Am I missing something for collisions. I am trying to cull these cylinders so there is no criss crossing (will delete longest).
however I am getting no collisions I have attached the debug view of the screenshot, these orange lines are not my mesh, but the debug view
Zooming in on a star I notice multiple cylinders per star (connecting the same stars) I am sure that I only have one, even if I duplicated by a bug it would be in exact place
yo i think im being affected by a nasty bug in Avian3d 0.3
so im using these packages
bevy-tnua = "0.24"
bevy-tnua-avian3d = "0.5.0"
avian3d = { version="0.3", features = ["3d", "debug-plugin", "parallel", "parry-f32","serialize","collider-from-mesh"] }
and basically .. if i do NOT put a 'transformInterpolation' component on my character, the bug doesnt happen. I can teleport my character (mutate its Transform in the Update loop) and it works just fine. but if I DO add a 'TransformInterpolation' component to my character, [which i need to do to make walking look smooth w the camera] then my teleport is broken.
the docs claim this
Note that changing Transform manually in any schedule that doesnβt use a fixed timestep is also supported, but it is equivalent to teleporting, and disables interpolation for the entity for the remainder of that fixed timestep.
but this is not properly happening for me
one thing that is odd (not sure iff this is OK?) but my character has a TranslationEasingState whose start and end are always Some . and by manually setting the 'end' prop i can get my char to teleport properly. But mutating my Transform WILL NOT properly teleport my character. And making matters worse, that is a private component so i cant just mutate it in code .
π¦
so for the time being i guess im going to have to disable the interpolation on my character. sad
now its super jittery compared to teh camera
Why not mutate the transform of the camera instead of the transform of the character?
without interpolation physic objects appear pretty jittery, iirc the phyisics timestep is 30 but and he's probably running the game at 60, even more noticeable at 120
literally half or a quarter of the smoothness
interpolation hides it well
Ye, I mean leaving Interpolation on rigid bodies and never fudging with the transform of them, then set the translation of the camera relative to the translation of the interpolated player
If I understood correctly, the problem they're seeing is that they need to teleport the player somewhere, but changing Transform to do this for interpolated entities is not working even though it's supposed to
Aaah that makes sense then
IIRC it worked for me when I last tried, so I'm not sure what's going on there π€
Remove the interpolation component, teleport, then add it again?
Or @vestal minnow adds API in general for "teleport this physics object so that everything works out" :p
I'm not sure if that works because TransformInterpolation adds components like TranslationInterpolation, RotationInterpolation, and ScaleInterpolation, and removing TransformInterpolation currently doesn't automatically remove those components
Really it should maybe be a TransformInterpolationBundle but that doesn't work as nicely with required components
Hmm probably should remove those in a lifecycle hook
Plz not π
Yeah, agreed
Hmm it could also be worth reconsidering whether it's even worth it to support interpolating individual transform properties, I imagine in the vast majority of cases you want to either interpolate the whole transform or nothing
Then we could have just TransformInterpolation, TransformExtrapolation, and a TransformEasingState, and not separate structs and systems for each individual property
Might need benchmarking to see what ends up being faster for the general case of interpolating the whole transform
With the current setup with each property being separate, some of the easing systems can run in parallel with each other
Nvm only the very cheap ones where it doesn't matter
(also physics uses Bevy's fixed time step by default so it's 64 Hz)
@vestal minnow is turning up the timestep hz a way to fix ghost collisions and tunnelling?
It doesn't really fix the core problem, but it does make tunneling less common since objects will be advanced in time in smaller steps
and results in more frequent collision checks
but like if you had an object travelling at a million units per second at a wall, it'd probably tunnel through at any reasonable time step unless you're using speculative collisions or swept CCD
ofc
Agreed, don't think that granularity is needed here. Maybe if we had separate components upstream for Translation, Rotation, and Scale I could see it, but we don't
And if people need it after all, I can imagine it's easy adding them back (with all current limitations)
thx
Draft PR for the PhysicsTransformHelper and fixing the global center of mass issues related to Forces
https://github.com/Jondolf/avian/pull/787
still need to add more docs for the force methods and test that it actually works
gonna try this out in a bit
I did run into a panic last night and then my cat immediately made a big mess so I didn't get to dig into it
thread 'main' panicked at forks/avian/crates/avian3d/../../src/dynamics/solver/constraint_graph.rs:168:9:
assertion failed: !is_static1 || !is_static2
huh? im trying to teleport the player to a different location
Has anyone ever ran into a bug, where once spawning a dynamic body with a specific linear velocity, it only applies the y axis ? In avian 3d
are you on main or using a specific release?
0.3
hmm, what you're describing is similar to a thing I'm seeing on main but that's related to the global center of mass talk above. I haven't really had problems with setting linearvelocity on spawn unless the entity I'm spawning is colliding with something else when it spawns
oh I haven't made a sensor that moves before, that's interesting
that clip is a little hard to follow.. are you saying the first run has Vec3::Z but the bullets always go upward?
I've pushed some more fixes to that branch now, fixing GlobalTransform, ColliderTransform, and mass properties being incorrect until the first time physics runs
On main I was seeing that applying an impulse in FixedUpdate for newly spawned bodies did nothing since their mass properties were zero because the mass property update ran before the collider had been properly attached to the body, but now it seems to work
Would y'all be fine with me changing Position and Rotation into a PhysicsTransform? I think this would more clearly indicate what they represent, and it would likely be better for both performance and internal stuff
API-wise it would mostly mirror Isometry2d/Isometry3d
I'm probably going to do that next unless someone complains :P
im here to complain, what needs complaining about?
whatever api decision you just took is annoying and not user friendly, but also whatever you changed it from is too. also my spacebar heating isnt working anymore
I like that it mirrors Transform somewhat. I don't have any solid objections
Ye sry I misunderstood π
yeah its weird -- my teleport (mutate transform) used to work even with the interpolation but now it doesnt anymore . not sure why
I am definitely mutating transform during Update (not fixed schedule)
so that sentence in teh avian docs seems incorrect
Sounds good. Can it just wrap isometry as a newtype?
I think we could yeah, though we probably want to duplicate some of the constructors and Mul implementation so that it being a newtype doesn't affect the API much
We also need f64 versions of the isometry types
Makes sense π
@visual sparrow @sweet sundial Here's a lil gift for you
https://github.com/Jondolf/avian/pull/788
yippee
You can get the largest contact impulse in a collision with contact_pair.max_normal_impulse(), and divide by the time step (i.e. Time::<Fixed>::delta_secs()) if you need the corresponding force
Or for a specific contact point, just contact_point.normal_impulse
i'm probably just going to use the first impulse over a threshold per collision
Getting a stable 9.81 N for a 1 kg cube resting on the ground with 9.81 m/sΒ² gravity
Oh HECK yeah!!
Thanks!
So many things I wanted to implement with this π
Glass shattering, playing different hit sounds, dealing damage, spawning a puff of smoke on enough impact, etc.
I tried normal_impulse for that in the past and found out that that was not at all what I needed haha
huh, specifically a Windows-only determinism issue?!

Yeah I think I've been accidentally gaslighting people by being like "just use normal_impulse" even though it's not at all what you'd expect it to be for gameplay purposes, I only recently realized it's not really the total impulse
hehe yep. The values I was getting for yeeted objects were similar to objects just chilling on the floor
I think I thought it was fine since if you divide it by the Time<Substeps> delta time, it is closer to the total contact impulse sometimes since you'd basically be taking the impulse from the last substep and extrapolating it as if that same impulse was applied at each substep, if I'm thinking about it correctly
but really you need to just add the impulses from each individual substep and the restitution pass into a separate value to get what you actually want
yeah I think this was somehow caused by the transform sync refactoring PR π CI is passing sometimes and other times Windows fails on this specific test
but so far it's just Windows and only this local determinism test, the cross platform determinism test is fine 
heh
a few fixedframes late sometimes
It should be computed right after the shapes have been found to be touching
what if you log it in FixedLast so it's after physics
(or in FixedPostUpdate and order relative to physics systems)
or in an observer for OnCollisionStart
an observer works too for me at least, and that is definitely guaranteed to be right after the solver has applied and computed the impulses
One caveat with this however is that speculative collision can affect what the initial impulse is
but so does the time step
oh dang i ran into this too for my bevy jam game! tried to use the normal_impulse to determine when to play a clang sound on heavy impacts, ended up being so inconsistent that i switched to just comparing velocity change between frames
love to see this fixed, loads of nice usecases for stuff like this π
switched to main. That did fix the spawning problem I had (the frame where global center of mass wasn't calculated.. obviously since now it doesn't exist, right?)
but yeah, how would someone teleport? I think there's something I need to reset
oh oh lol PhysicsTransformHelper
cool that works. I didn't fully get why it returns a mutable position/rotation because... my first thought was "oh, I update the entity's transform and then call update_physics_transform" but then I saw it returns the mutable position/rotation so then I thought "oh, maybe I modify these directly"
but it's actually the first one. And I had to use a ParamSet so that I can modify the Transform first and then use the PhysicsTransformHelper.
it's not terrible for supporting this kind of thing, but it did confuse me for a minute
Mm I made it return those so that if you needed them for something, you wouldn't need to query for them again for no reason, but I can see how it's confusing
We should probably add a teleport and maybe a teleport_local helper to it as well
That would help @marble cradle as well, if the helper also deals with interpolation
yeah, I guess my thinking was.. if I modify the position/rotation afterward then wouldn't I need to call update again?
I am occasionally getting this
thread 'main' panicked at forks/avian/crates/avian3d/../../src/dynamics/solver/constraint_graph.rs:168:9:
assertion failed: !is_static1 || !is_static2
I think it happens when I enter sensor colliders
it might be a specific sensor which changes the rigidy body and does some other things
Yeah that's the only way that can possibly happen I think
"surely he's not doing this"
Contact pairs cannot be created between static-static pairs since they're skipped in the broad phase, but if you're changing the body type while the contact pair already exists, I guess it currently doesn't remove the pair and handle it correctly
hm, but I should be OK if I change the body type before the physics stuff happens?
hmm... I switch the rigidbody while OnEnter'ing into a new State... I would think that'd happen prior to physics, right?
yeah . making 'teleport' be a first class citizen in Avian would be really nice im o
bc just having us mutate transform no doubt is bound to create lots of undefined behavior and crazy edgecases
ub? dang how much unsafe is in here π
Not literally undefined, but "physics interaction that looks buggy"
poorly defined
I attempted to place cylinders at origin but I still cannot detect examples, this is roughly a minimum reproducable example, only crates used are bevy and avian3d
Sorry, what's the expected result here? They do collide at the start
(I'm running this on main)
2025-07-22T12:00:59.596653Z INFO bevy_diagnostic::system_information_diagnostics_plugin::internal: SystemInfo { os: "Linux (EndeavourOS rolling)", kernel: "6.15.7-arch1-1", cpu: "AMD Ryzen 7 7800X3D 8-Core Processor", core_count: "8", memory: "30.5 GiB" }
WARNING: radv is not a conformant Vulkan implementation, testing use only.
2025-07-22T12:00:59.641256Z INFO bevy_render::renderer: AdapterInfo { name: "AMD Radeon RX 9070 XT (RADV GFX1201)", vendor: 4098, device: 30032, device_type: DiscreteGpu, driver: "radv", driver_info: "Mesa 25.1.6-arch1.1", backend: Vulkan }
2025-07-22T12:01:00.066338Z INFO bevy_render::batching::gpu_preprocessing: GPU preprocessing is fully supported on this device.
2025-07-22T12:01:00.098764Z INFO bevy_winit::system: Creating new window App (0v1)
2025-07-22T12:01:00.098829Z INFO winit::platform_impl::linux::x11::window: Guessed window scale factor: 1.6979166666666667
2025-07-22T12:01:00.100215Z INFO cylinders: cull
2025-07-22T12:01:00.100922Z INFO cylinders: loop
2025-07-22T12:01:00.108594Z INFO cylinders: collisions
2025-07-22T12:01:00.111357Z INFO cylinders: collide set
2025-07-22T12:01:00.111368Z INFO cylinders: after
2025-07-22T12:01:04.545192Z INFO bevy_window::system: No windows are open, exiting
2025-07-22T12:01:04.546151Z INFO bevy_winit::system: Closing window 0v1
Hmm I am not getting a collision
will try main
Switching to main fixed it
I also tried simd on and off doesn't make a difference, so something in main fixes this.
Thanks for your help!
the Windows local determinism thing is killing me, I don't understand what it could be
I've checked and there shouldn't be any scheduling ambiguities, and if there was, then the Linux and MacOS tests should also fail sometimes
the only thing I can think of is if I somehow introduced some math operation that is non-deterministic across calls but I don't think I did?
or maybe like something where there's a dependence on how long a frame takes and the Windows runner in CI is slower than the others, but the test advances time in fixed steps
Scheduling ambiguity maybe?
The run order of observers and hooks is deterministic right? That's the only ambiguity that isn't/can't be checked by CI, the physics schedules already error if there are ambiguities
but again even if that wasn't deterministic, I don't see why only Windows would be the problem
wait surely it's not this lol
The transform sync refactoring PR uses Rotation::angle_between to determine if the old and new transform are equal within an epsilon
Quat::angle_between internally uses a custom acos_approx_f32 function for std math, which is fully deterministic
However DQuat::angle_between uses f64::acos for std math, which is documented as
The precision of this function is non-deterministic. This means it varies by platform, Rust version, and can even differ within the same execution from one invocation to the next. This function currently corresponds to the atan2 from libc on Unix and Windows.
nooOOOOOOOO
thatβs subtle haha
Good thing you have those different platforms in the test matrix!
But CI should be using enhanced-determinism, which uses libm, so I'm not sure why it breaks there π€
i guess note to self, never use DVec3::angle_between or DQuat::angle_between
or DVec2::angle_to
Or DVec 
Nope that's not it I don't think, Windows still fails with the f32 version π
well I booted up Windows and can at least actually reproduce it locally now instead of spamming CI
it's only with f64 but I'm not sure if that's just a coincidence or not
Update: It seems like it wasn't the transform sync refactor PR, nor is it a math determinism issue
Instead we appear to have UB in the solver 
why the test only started failing on Windows and after that PR, no idea
oh lol yeah we currently par-for-each over the overflow constraints too, even though that should be serial
just checking, this would happen regardless of if I'm going kinematic -> dynamic or vice versa?
I think it'd happen if you go from kinematic or dynamic to static so that both bodies in the contact pair are suddenly static
hmm... I'm switching between kinematic and dynamic π€
actually, I guess I am switching the rigid body while inside of a static sensor
the check that triggers that panic is
let is_static1 = contact_pair.flags.contains(ContactPairFlags::STATIC1);
let is_static2 = contact_pair.flags.contains(ContactPairFlags::STATIC2);
debug_assert!(!is_static1 || !is_static2);
and the static flags are set in NarrowPhase::update_contacts like
contacts.flags.set(ContactPairFlags::STATIC1, is_static1);
contacts.flags.set(ContactPairFlags::STATIC2, is_static2);
where is_static1 and is_static2 are body.rb.is_static()
(where body.rb is the RigidBody)
so as far as I can see, the only way the panic can happen is if both rigid bodies are static
as part of the graph coloring yeah
ok you're not going to believe this.
turns out I was switching a pile of dynamic rigid bodies all into static
Okay this should fix the UB and determinism issue
https://github.com/Jondolf/avian/pull/790
I'm running into an issue where... it seems like if I add a ColliderDisabled and then remove it... the collider still behaves like it has ColliderDisabled
I'll take a look at that later, I'm opening the #physics-dev channel discussion in #community now :)
cc @zinc talon
hah, observer footguns strike again!
the broad phase has this
app.add_observer(
|trigger: Trigger<OnRemove, (Disabled, ColliderDisabled)>,
query: Query<AabbIntervalQueryData, Without<ColliderDisabled>>,
// ...
It is supposed to react to re-enabling colliders. Two problems:
Disableddoesn't work because, again, we're not explicitly including it in the queryColliderDisableddoesn't work because we have aWithout<ColliderDisabled>filter, andOnRemoveis triggered before the component has actually been removed
I'll try to fix the static-static contact panic tomorrow, I'll probably make RigidBody an immutable component so I can react to changes via observers
ooph, yeah, I keep thinking Disabled is tricky
I'm curious, is there a reason that something like acceleration structs don't exist?
As in, subtracting LinearVelocity from a different LinearVelocity to get a LinearAcceleration. I can't seem to find other physics engines that have it either which makes me think its just not common or necessary
if you add or subtract two velocities you get a velocity, that's dimensionally consistent
and actual systems of measurement are probably out of scope anyway
try https://docs.rs/uom though
lol my fault, I described the setup very poorly. I have my delta v and delta t and was just curious if there was a system of structs in place for accelerations
I guess my question is more aimed at something like a MaxLinearAcceleration component or otherwise and getting that from said dv/dt
On the main branch we have ConstantLinearAcceleration and ConstantAngularAcceleration
Probably different from what you're asking for though?
BTW will 0.4 fix those TODOs on the requires of RigidBody that give ExternalForces/ExternalImpulse/etc to static bodies? π€
Disabled is annoying enough in observers that I just defined an alias:
type AllowDisabled = Or<(With<Disabled>, Without<Disabled>)>
```Of course on bevy main we already have Allows but you know ... we're not on main :')
Bonus points if you can't figure out if your app is crashing because the query failed because the entity was disabled or because it was despawned after the trigger was fired but before it ran 