#Avian Physics
1 messages · Page 29 of 1
Hello, I'm having trouble understanding the problem here.
My character has no problem jumping over that box, but when I run into the box and jump I just much lower.
I removed all friction and set the friction to ZERO with CombineCoefficient(Min).
I am using a kinematic body with the default collision code from the kinematic character controller
neat
thank you
LinearVelocity is good for character controllers right?
why does avian use its own vectors and for 2d there is no z?
I couldn't get my controller to feel responsive with linear velocity, so I made a disgusting monster. shape_cast to get as close as possible without clipping and then apply an external force on whatever you're supposed to interact with
yeah, if it's not kinematic
and it's 2d because that's what the sim needs, and there are plans to split bevy transform into transform3d and 2d anyway
if it's kinematic then that's essentially what you're supposed to do
That's awesome. Today I spent 20 minutes figuring out how to slide diagonally when against a static rigidbody, then lost hours making sure it doesn’t get stuck on sharp corners lol
look up collide and slide, there are a few people working on the exact same thing including jondolf 😅
😮 I landed on the same collision epsilon. Thank you!
Mine is in 2D, I had to increase collision_epsilon from (f32::EPSILON * 50) to (0.01) to avoid clipping on sharp edges. Why do sharp corners need extra caution? Is it float precision errors?
Probably some problem with the kinematic character controller's collision logic, though IIRC it doesn't have this issue in the example scene 🤔
The character controller there is not good though, typically you would want a proper collide-and-slide algorithm for kinematic characters
Based on basic profiling yesterday, it looked like Query::get_many_mut in the solver is costing us more than the constraint logic itself 😬 get_unchecked was slightly better but still dominates performance
I probably need to double-check though, it seemed weirdly bad
We will probably move to storing separate SolverBody structs outside the ECS for the critical solver loop anyway though, which should hopefully fix this partially
so when using linearvelocity, and 2 objects collide, for some reason they rotate? i dont have any code that does the rotation, it rotates as if there was friction (which there probably is, so how do i disable that)?
If you don't want any rotation (and your bodies are dynamic) you can use LockedAxes::ROTATION_LOCKED. Friction you can remove with Friction::ZERO. If you're only setting it for one of the bodies though, then you might also need .with_combine_rule(CoefficientCombine::Min) since otherwise it uses the average friction of the two colliding bodies by default
if i was to use a kinematic body how does it not fall through the floor? I wanted to use that but they dont collide at all, am i doing it wrong. Can I disable Friction for everything?
There should be a DefaultFriction resource that you can set to configure default friction globally
okay nice
the constant doesnt exist
Kinematic bodies are like static bodies in that they have basically infinite mass and therefore don't respond to collisions, joints, etc. but unlike static bodies they can be moved and have velocity. This is the same as in other engines. Typically you'd have something like a collide-and-slide algorithm for kinematic character controllers to move them and handle collisions, there's a few implementations floating around here but no built-in thing yet
oh okay
You can do DefaultFriction(Friction::ZERO)
should probably add the constant for DefaultFriction directly too though
thank you :)
thank you for all the help :)
happy to help 🙂
@vestal minnow Mr Jondol, I am making a hitbox for my melee weapon. The hitbox usually follows along the movement of the bone. In this case, question tho. How can I spawn a tri mesh that follows along a scene?
oh
hi just implementing a basic collide and slide (this might be me being dumb)
i have a part where i'm shapecasting down the slide velocity to detect any second walls to stop at but i don't seem to be detecting them.
i've got a blue gizmo to show the character "collider" and the green gizmo is the end of the slide velocity cast.
I'm using move_and_slide for a top-down 2D controller. To push dynamic objects, I increase their velocity when the player collides with them. However, objects with high linear damping slow down too quickly, causing jitter when the player catches up to them
My plan is to fire an event when an object is pushed, storing its original LinearDamping in a HashMap. While pushed, damping is set to 0.0, and once released, it’s restored. Does this approach make sense, or is there a better way to handle this?
Do raycasts ignore the parents of their parent entities by default if they're set to ignore their parent?
wait does avian not support multiple shapecast hits against the same collider? i'm using a compound collider for the environment so that could be the reason
It doesn't I think, Parry only returns one hit
It should pick the closest hit, but for penetrating cases it might be arbitrary 🤔 taking a brief look at the code it doesn't seem like it considers penetration depth to prioritize hits
They ignore the collider of the RayCaster entity itself by default, but not parents
ah that's really annoying considering i want to ignore any hits to the same wall on the second shapecast - i should probably use an epsilon to avoid this then
You may be able to call contact_query::contact_manifolds for your cast shape and the hit shape when you detect shapecast hits with a distance close to zero, to manually compute more than one hit
a bit more expensive and complicated but might work?
okay epsilon just makes it work™️
although it'll still have issues on slopes because it'll return the hit on the slope first so i need to rotate to account for that
If I may make a suggestion, one thing that always burns me with Avian is how for ex: Collider::cuboid() takes in full size values, but Bevy's Cuboid takes in half size values.
It would probably make more sense to make it use half size too? It just feels unexpected
I understand that would be an annoying change but might be for the better
Cuboid::new takes full size
The collider constructors match the shape constructors, not internal representations
wth ok im just crazy
ignore me
I dont know why I havent been using new... embarassing lol
if I tell it to exclude it's parent entity though
does it then exclude its parent's parents?
Like, exclude it by adding the parent entity to the excluded entities in the SpatialQueryFilter? That only excludes that specific entity, not its parents
Then there's probably a bug in my code, thanks
@vestal minnow I think I used too much polygons here right? HOHOH
I'm not sure how to use num_subdivisions
I looked it up in the server and it seems like nobody ever asked about it?
So I have a body that I've assigned a LinearVelocity, and am getting the GlobalTransform of. When I log and plot the components of the LinearVelocity (vx, vy, vz) * a delta_t (from Time<Virtual>) vs. the actual GlobalTransform's global_transform.translation(), everything lines up.
However, when I do something like get the heading from global_transform.rotation(), then doing something like linear_velocity.length() * sin(heading) and linear_velocity.length() * cos(heading), I'd expect that the resulting values are the same as in LinearVelocity vx, ,vz. However, I'm seeing (meaningfully) different values there. This leads to a very different overall position plot that the one from either GlobalTransform directly (as propagated by the physics plug-in) or my integration via LinearVelocity * dt. Is this expected? Am I missing something/doing something dumb?
@vestal minnow forgive me if this is documented somewhere, but have you thought about how avian could fit into a networked physics scenario? I'm thinking about things like rewinding world state and re-running simulation steps with "past" inputs.
EDIT: just found an example using lightyear: https://github.com/cBournhonesque/lightyear/tree/main/examples/avian_physics
does linearvelocity component already multiply everything with delta time?
Is there a reason why that sword needs so many faces?
He didn't use textures, normal map and smooth shading
(I suppose)
Avian seens to have some non deterministic issue, with contact points. Where they are calculated individually, in a non-sorted manner. Which causes rollbacks
Besides that if you base your character controller on floating point characters and just use sensors you can circumnavigate that issue
I'm not 100% sure how it works since the actual logic is implemented in Parry, and Avian just passes the parameter forward to it. I think for something like a circle though, it just corresponds to how many segments or vertices there are, for example with 6 subdivisions you'd get a hexagonal shape. Similarly, for a cone or cylinder, the base(s) would have 6 vertices. For spheres or capsules it might be more complicated, and it'd presumably control the resolution for both phi and theta (two angles used for constructing the round surface)
yes
thank you :)
If you're asking in the context of Collider::set_scale, note that scale for colliders on entities is set by the Transform scale, so using the method might not do what you expect
I'm not sure I see the friction being applied in the video. What exactly is going wrong in the video?
i dont want the friction
in the image on the left you see its Zero
Do you mean how the objects push each other around? It looks to me like the bodies are moving into each other, which pushes them independent of whether friction is applied or not
i dont want the bodies to rotate
To clarify, LinearVelocity stores velocity in units per second (where "units" is typically meters) so the value itself isn't scaled by delta time. But when the engine actually applies this velocity, it does multiply by delta time to get the change in position
okay so i dont have to * by delta myself ?
You don't, no
okay
Unless maybe if you're applying acceleration since the SI unit for that is meters per second squared
then you probably want to use this I'm guessing https://docs.rs/avian2d/latest/avian2d/dynamics/rigid_body/struct.LockedAxes.html
A component that specifies which translational and rotational axes of a rigid body are locked.
can i have that for everything by default?
So if you're setting velocity or applying an individual impulse, you don't need delta time, but if you're adding to velocity continuously to accelerate something over time, you might want to multiply by it
okay
don't think so, but if you want to have some objects not move at all you can just not spawn a rigid body for it, only spawn a collider
i want everything to move, altough the rotation should be locked, i dont want the player to walk sideways or something
A hacky way that might work would be to make LockedAxes a required component for RigidBody. Something like this IIRC
app.register_required_components_with::<RigidBody, LockedAxes>(|| LockedAxes::ROTATION_LOCKED);
yeah i will probably do that
You can still override it for individual entities by adding LockedAxes for them manually
i.e. to allow rotation you could add LockedAxes::new()
the part with the lambda doesnt work? its a "redundant argument"
i dont think the method takes that in
oh
i forgot the _with
Oh I see
Thanks
usually melee weapons use box collider that's like 2-10 times bigger than weapon itself depending on what feels best for the game, then just cover it up with a sexy weapon trail
trimesh is almost never used for anything dynamic out of performance concerns, and nobody notices that kind of stuff anyway
How do usually debug your colliders?
Using a circle gizmo for a circle collider is trivial, but what about capsules?
you can add the PhysicsDebugPlugin
A plugin that renders physics objects and properties for debugging purposes. It is not enabled by default and must be added manually.
Oh nice!!
Thanks!
you can also do something like this:
gizmos.primitive_3d(
&Capsule3d::new(radius, length),
Isometry3d::new(position, rotation),
color,
).resolution(6);
Woah
I didn't know you could do that
@vestal minnow Sorry for the direct ping, but was just curious if you might be able to point me in the right direction? 🙈
I'm not sure I fully understand what you're doing. Are you plotting what the expected trajectory of a body (projectile?) should be? What's the "heading" intended to be?
I assume the "heading" is meant to be the direction the body is moving but the rotation doesn't necessarily tell you that, unless this is for like character movement or a car where you move in a specified forward direction
Oh, sorry! So basically at the end of a simulation, I'm trying to calculate where the body was at a given point in time using the linear velocity information and heading, then compare that to the simulation's actual position information as a ground truth. I was trying to get the body to move like a car, which I'm doing by applying all the forward movement force being in the body's frame by gt.forward() * force
I sort of assume that the "compass" on the body is always correct, but if I was calculating speed via wheel rotations/odometry (linear velocity in the vehicle's direction of travel), can I still do a decent estimation of where the vehicle was compared to the sim's groundtruth?
this still doesn't seem like the body velocity is necessarily aligned with it's global rotation
only the force is, which when integrated over various orientations, will result in a velocity direction that can be different from the cars rotation
Hmm, that does make sense. I have some drag that I thought might cancel that out, but maybe the solution is assume really good traction, and do something like lv = lv * gt.rotation() on each timestep so that linear velocity is conserved, but always in the direction that the vehicle is traveling?
If you want to "simulate" the full motion, you can just integrate the velocity, i.e.
velocity += forces / mass * dt
position += velocity * dt
which requires that you also consider that drag in the forces
if you constrain the physics so that drag is really strong such that any motion that is not aligned with the rotation immediately is cancelled, than the simple approach should align closer, yes. Though I think I'm still not sure what you're ultimately trying to accomplish
I'm sort of trying to avoid simulating all the physics, because I think that's what Avian already does? Basically using a subset of values to back out overall position
So I'm doing some simple robotics, and I have a compass on the vehicle and a way of measuring/estimating forward velocity (i.e. wheel speed -> n m/s). I'm developing a sim for some of the behavior stuff (like going from point A -> point B), and am using a Bevy sim to try to develop the code responsible for estimating where I am in the world
Yeah I guess I don't know for what reason you're trying to approximate what avian already does, so it's hard for me to judge what approach would be best for you
usually, car games explicitly allow for the velocity not always aligned with the car rotation, so drifting is possible
Ah ok, starting to understand
So if I can develop some code using the inputs that I have (direction, speed) and some simplifying assumption (linear velocity is basically conserved), then when I take the code out of sim, I can still estimate the vehicle's position. Before I do that, though, I'd like to double-check that the math I'm using is correct, so I'm trying to ground-truth the code I'm using to estimate position based on my inputs to the full Avian-simluated physics one
In that case, your initial approach does seem to make sense. How accurate it is then depends on how closely your simulation is aligned with just a heading aligned velocity, but there might be an implementation issue somewhere ofc.
In your case I would do things like add debug gizmos to plot all your orientations and velocities, and maybe intermediate position predictions and stuff like that and see if things make sense
it's hard to judge without the full code what might be going wrong
Gotcha! Do you happen to know if Avian PhysicsDebugPlugin (I forget if that's the right name) has that functionality? Or should I maybe just start ground-up with something like bevy_gizmos?
I think the Plugin by default shows all colliders and things like avian shapecast components etc.
I would definitely use custom gizmos to debug your own simulation logic though
because then you can draw all the custom vectors and positions that you use in the simulation separate from avian
Awesome, I really appreciate the advice 🙂
np, glad to help
also, make sure to use the correct delta time. if you use a custom loop then it should be straightforward, but if it's a repeating system you need to make sure that the delta time is the correct one for the schedule. (For Update it should just be Res<Time>::delta_secs though). There is also Res<Time<Fixed>> for FixedUpdate
iam using a capusler collider and its seens its not centered on the model, its possible to change the collider transform?
its floating lmao
commands.spawn((
SceneRoot(
asset_server
.load(GltfAssetLabel::Scene(0).from_asset("Basic.glb")),
),
RigidBody::Dynamic,
Collider::capsule(1.0, 0.0),
Player,
Transform::from_xyz(0.0, 0.5, 0.0),
));
Why does the capsule have a height of zero?
I think that should technically be equivalent to a sphere since it only has the radius
You can transform colliders separately from the mesh though by making the collider (or the mesh) a child entity and adding a Transform to that
@vestal minnow i thought you already changed default layer to 1, why does gltf still spawn as 4294967295
and adding layer component still doesn't change anything v_v
should be mem.. sec
memberships only the first
yeah it's memberships
oh ColliderConstructorHierarchy apparently still defaults to CollisionLayers::ALL 
filter is same
are you using that?
for now you could do .with_default_layers(CollisionLayers::default()) lol (for the collider constructor thing)
I'll open a fix for this asap though
Is like the general Avian thread?
I have a weird niche use case I'm working on and I want to get a local copy of avian3d in my project directory so I can experiment with making my own workaround
git cloning the avian directory does not seem like what I want but I don't know
found cargo-download nevermind
😄
Is it possible to convert a Vec<CollisionLayers> into the LayerMask that "memberships" and "filters" expects? Kinda a rust noob but would I need to implement "From" for Vec<CollisionLayers> somehow, I'm imaging looping and doing bitwise Ors through the Vec in some kind of collector?
For reference this is what I'm trying to accomplish and then pipe into the collision layer targets.
pub enum DamageSource {
Player,
Enemy,
Environment,
}
impl DamageSource {
pub fn to_collision_target(&self) -> Vec<GameCollisionLayer> {
match *self {
DamageSource::Player => vec![GameCollisionLayer::Enemy],
DamageSource::Enemy => vec![GameCollisionLayer::Player],
DamageSource::Environment => {
vec![GameCollisionLayer::Enemy, GameCollisionLayer::Player]
}
}
}
}
yeah with fold you could do something like this
let combined = layers
.iter()
.fold(CollisionLayers::NONE, |mut acc, curr| {
acc.memberships |= curr.memberships;
acc.filters |= curr.filters;
acc
});
producing CollisionLayers that has all memberships and filters of the elements in your Vec<CollisionLayers>
Good old |=
see this is magic I could have never cooked up ty ty
Could it be that I can only use collider_name.set_scale in a specific timing or something? I use it but the default physics plugin overrides it
Wait is it automatically connected to Transform.scale
It is yes #1124043933886976171 message
Ohhh
Thanks
Is there a way to override that?
(I don't need it right now, just curious)
Do child Colliders inherit the CollisionLayer of the ColliderParent, or is it set to the defaults if I omit CollisionLayer on the child collider?
Seems like they're not inherited on closer inspection and I got things working.
@vestal minnow its possible to see the colliders?
its ahrd to figure out hwo stuff are working
A plugin that renders physics objects and properties for debugging purposes. It is not enabled by default and must be added manually.
so basically you can do app.add_plugins(PhysicsDebugPlugin::default());
thanks sir
Hey 👋 Getting this bug where some dynamic objects (mostly those with FixedJoint or RevoluteJoint constraints) suddenly "disappear" (but it turns out their physics state is getting filled with NaN). I can't reproduce it 100% exactly, but it seems to happen more frequently with objects that are really wide in one axis and really narrow in another, or when multiple objects of very different sizes interacting with each other.
I have a revolving door made out of a few dynamics objects tied together with FixedJoints and RevoluteJoints around a pivot kinematic object that is particularly problematic, and I managed to make the bug happen semi-consistently by cutting up one of the door panels into smaller pieces of odd shapes/angles. (All Colliders are convex hulls)
I've managed to track down what I believe is the first occurence of a NaN to here: https://github.com/Jondolf/avian/blob/c4840ddf4a3adb081bb21c992e524231df83e195/src/dynamics/integrator/semi_implicit_euler.rs#L135
Adding this line below it:
if !delta_rot.is_finite() {
panic!("ang_vel = {ang_vel}, scaled_axis = {scaled_axis}, delta_rot = {delta_rot}")
}
Makes the app panics with the following output:
hread 'main' panicked at contrib/avian/crates/avian3d/../../src/dynamics/integrator/semi_implicit_euler.rs:143:17:
ang_vel = [-8648430700000000000000, 2211794600000000000000, 4786264000000000000000], scaled_axis = [-22521959000000000000, 5759882400000000000, 12464231000000000000], delta_rot = [NaN, NaN, NaN, NaN]
Which seems to indicate that something is making the angular velocity reach incredibly high values (numerical instability?) that then produce the NaN, but I haven't been able to find out where exactly that might be happening.
The fact that shapes of irregular sizes/different dimensions make it happen more often makes me suspect it could be related to gyroscopic torque, but honestly no idea, as commenting out this portion of the code doesn't really make it go away: https://github.com/Jondolf/avian/blob/c4840ddf4a3adb081bb21c992e524231df83e195/src/dynamics/integrator/semi_implicit_euler.rs#L82-L88
Anyone with a physics-based player controller tweaking FixedUpdate timestep?
I used a transform-based player controller for precision since physics felt unresponsive for a bullet hell. I wrote a long but working solution for physics interaction (get close and apply external force), but I just realized that I can increase the timestep and go fully phyiscs for the player controller, why is this a bad idea?
Proposed fix for the issue described above/captured in the video
Can't really vouch for the “correctness” of the approach, but since MaxLinearSpeed and MaxAngularSpeed are already non-physically realistic, it should hopefully be fine?
Is there a way to modify the points of an existing collider? I have some dynamic terrain (a plane) with a collider. When the mesh changes (perhaps every frame) I remove the collider component and re-add one with ColliderConstructor::TrimeshFromMesh.
This feels inefficient, and also seems to have a kind of timing issue where objects will occasionally fall through the ground when the collider is removed and re-added.
Are you mixing in kinematic elements (setting position or velocity)? Or is everything dynamic (controlled by forces) or static?
update can run anywhere from 1 fps to 10000, so even if you're multiplying by delta you'll start to teleport around at low fps
fixedupdate tries to run at usual intervals even at 1 fps, and since you don't have to multiply by delta the teleports don't happen even if something goes wrong
but tbh nobody's gonna play bullet hell at 1 fps so maybe it's not an issue either way, even if they tunnel through some bullets they'll still bump into another one D;
Depending on your setup, if you have objects with small and large masses interacting with each other, it could maybe be related to how typical physics solvers struggle with high mass ratios. I wrote up this explanation on the Dimforge Discord at one point #747935665076830259 message
I would suspect that the problem in your case is probably mostly the joints though, or how they interact with things, Avian's joints aren't particularly stable with the current implementation that is still using XPBD
There isn't really, I don't think Collider has a method to mutably access the shape (though it probably should). Even if you could access it, Parry's TriMesh doesn't allow mutable access to mesh data, aside from a couple of specific helper methods
I think updating vertices would involve several other things like updating the pseudo normals and Qbvh. It would be nice if there were some methods for this though
@vestal minnow any plans for a release date for avian 0.3?
The player character controller is kinematic, but the issue can happen without the player being even close to the affected objects. I guess the anchor object used for the revolute joint is also kinematic, but it could probably also be static, I think. I doesn't move
Oh yeah I think this is almost exactly my case, since for some of the objects I do have "chains" of fixed joints
enforcing the speed limit continuously for all bodies during solving, even with a relatively high limit (e.g 1000) has really helped with the stability
I don't really love enforcing it in the constraints like that since that's three or six extra normalizations and more branching per contact point, per body, per substep
Hmm yeah that's fair, I haven't really thought about the computational cost of it. How many contacts per number of bodies can one typically expect to take place? Is it 2x? Or more like 10x?
Depends on the shape pair, for two spheres there's 1, for two cuboids with a face contact there's 4, for things like triangle meshes there can be more
I wonder if annotating some of it with unlikely() once that is stable would help the compiler optimize it more?
It's also pretty likely that the constraint solver won't even have access to MaxLinearSpeed and MaxAngularSpeed after some optimizations I have planned
the core solver (excluding position and velocity integration) probably wouldn't access the ECS directly, but would have its own more compact SolverBody structs that have only the data required for solving the constraints, like velocity and maybe some mass properties
this is similar to what e.g. Rapier and Box2D do, where the solver has its own optimized data types separate from the user-facing body definition
which will also be relevant for AoSoA style SIMD
(for example Box2D has this b2BodyState)
Hmm yeah you can then make it a much tighter loop that way, and cache-wise it will probably also help
It should hopefully also help fix the bottleneck where Query::get_many_mut seems to be taking us more time than the actual constraint solving 🙃
So far I've done one Avian release per Bevy release, so it'd be once Bevy 0.16 is released
Seems reasonable
There isn't that much new stuff merged that'd be in a 0.3 release yet, mainly just collision hooks, PhysicsPickingFilter, and a few fixes
For 0.3 I'd ideally like to get in the contact graph, better contact management and contact types, maybe simulation islands, and maybe support for multiple physics worlds (if the indexing PR gets merged)
and a bunch of miscallaneous other stuff
simulation islands might still require more work on internals so we'll see
In general I'm focusing more on the collision detection and solver internals atm since a lot of it is implemented in a pretty naive way in terms of performance
Is the plan to migrate away from XPBD for the joints at some point, then? Would that help with the difference of masses scenario? Is there some algorithm that deals with an entire chain or joints at once instead of just pairs?
The plan is to migrate to an impulse-based solver for joints, like what contacts already use. It probably wouldn't help much with mass ratios, but might be more stable in other ways, and would make joint motors, predictive limits, and better stiffness tuning (with a frequency and damping ratio) easier to implement
For solving entire chains, there are solvers using reduced coordinates like Featherstone's algorithm
solvers like that are pretty rare for game physics and more expensive than the standard iterative solver, but Rapier does support it with its multi-body joints, and PhysX also has its articulations which are similar
I don't have immediate plans to add multi-body joints / articulations, but they would be cool to have eventually
(Box2D, Jolt, Bepu, etc. don't have them either afaik)
Oh yeah being able to explicitly add N bodies to the same joint would be an elegant way of adding this without having some sort of generalized graph walking mechanism that does it automatically, but even then I can see it being challenging to implement
BTW, in this part of the code, if one of the bodies is static, or has a higher dominance, isn't their part of the impulse "lost"? Would it make sense to have an else that applies the remaining impulse to the other body instead? Effectively treating it as if it had infinite mass/inverse mass of 0?
That should be taken into account by the effective mass which is used to scale the impulse
Oh there's another matching branch elsewhere that doesn't tally their mass to the total if they're not dynamic, then?
or like the impulse is the correct impulse and it is theoretically applied to the static body too, but since it has infinite mass, it does nothing to it
the inverse mass returned by effective_inverse_mass is zero for static bodies
and kinematic bodies, and bodies with locked axes
I'm not sure if dominance is handled correctly though 🤔 might not be
oh since it can't know ahead of time what other body it will be interacting with, and can't change the effective inverse mass
one way to handle this could be to store the effective inverse mass and inertia for each body in the constraint itself, initializing them in generate based on which body is static or has higher dominance
this would also remove some branching and make it so that it's only computed once per time step, not per substep
at the expense of maybe a bit more memory usage
but it will still be calculated multiple times per object if it appears in multiple contacts, instead of just once at the computed mass level?
once per constraint between two bodies, but only in ContactConstraint::generate (which is before the substepping loop) and not in warm_start, solve, and apply_restitution (which are inside the substepping loop)
no that doesn't make sense nvm
we just need to change ContactConstraint::generate to consider dominance for the masses I think
the other methods don't really care about the mass of the other body since the effective mass was computed already
Hey, I'm using Time<Virtual> to control the simulation speed in my game. I synchronize Time<Physics> with it using this system:
fn update_physics_speed(source: Res<Time<Virtual>>, mut physics: ResMut<Time<Physics>>) {
physics.set_relative_speed(source.relative_speed());
}
This works, but I'm noticing on higher speeds (like 8x), avian2d starts causing major performance spikes.
I ran a profile and this seems to be because avian2d is running a many substeps during that frame, which makes sense...
But I'm wondering if there is a solution? Is there a more correct way to synchronize physics/virtual times which doesn't cause this issue?
Substeps exapnded:
I'm pretty sure changing the speed of Time<Virtual> just causes FixedMain to run more frequently, which also causes physics to run more (since it's in FixedPostUpdate by default). It doesn't affect the number of substeps per fixed time step; in your image, it still seems to do 6 per time step, which is the default
(by "substeps" I mean the substeps taken by the physics solver)
You could scale down the fixed time step inversely proportional to your sim speed, so 64 Hz at a speed of 1, 32 Hz at a speed of 2, and so on. But this can make the simulation drastically worse once the frequency is low enough.
It also causes the annoying issue of the simulation behaving differently at different speeds, which makes it hard for players to predict what will happen based on just things like things moving quickly
Yup
For sped up simulation your choices are basically
- Run physics more frequently, keeping consistent and stable behavior, at the expense of worse performance
- Run physics at the same rate as before, keeping performance roughly the same, at the expense of worse simulation quality and behavior being dependent on the simulation speed
There is also the option of not actually speeding up the simulation, and instead having things move and accelerate faster, which has the benefits of being predictable and efficient, but requires a lot of extra engineering work and creates a lot of room for bugs
I guess this is one of those cases where there are 3 things you want and only get to choose 2 :')
fwiw in my game I just speed up the whole simulation, since it's essentially free and I shouldn't need more than a few % to keep things in-sync, and running ~70 ticks/s instead of 60 ticks/s is nothing when I also run like 14 ticks of rollback each frame 😂
- Run the simulation on a generative AI that just predicts what happens

Or without the AI: extrapolate the effect of one tick by multiplying the changes by 8 ... Things definitely won't ghost trough walls all the time 😂
there was one physics engine that legit did something like this and their performance claims were pretty insane, though I do press X to doubt
I bet they still lost to bepu 😂
oh this I think
https://github.com/Genesis-Embodied-AI/Genesis
it's more than just physics though
Man I swear to god if I import one more godamm parry3d hashmap not wanting too I am gonna shot myself
lmao why does Parry of all things re-export a hash map
oh it's for determinism I think 🤔
Guess we have a new avian meme now 🤔
what's a good keyboard shortcut for toggling the visibility of the diagnostics UI thing in examples?
I'm currently using Alt+P but idk if that's weird
just P is for pausing already
I don't think there is much of a convention for hiding UI ... I've seen some games do ctrl + H ... Which seems rather arbitrary too 😂
Some examples also just throw everything on F1-12
Not just in browsers, Ctrl + P is for printing in any app that supports it I'm pretty sure
mm yeah
Could just do U for UI 
I'll probably do that lol
I think I'll have a smol text in some corner to display the keys anyway
Like one of those annoying game tutorials that just won't shut up until you press W A S D Space and Shift in that exact order 
Not a big fan of the quotes but that seems fairly unintrusive 🤔
Might even make sense to flip it, you want to know the key for pause/unpause, not the function of P
that reminds me of ubisoft's AI ragdoll motion-matching, i wonder what happened to it
doesn't look like there's anything new since GDC 4 years ago
if it doesn't have to be just 1 key then maybe shift/ctrl/alt+A(vian)+all avian debug keys?
these keybinds are just for the examples so there's no risk of conflict for actual apps, so I'm keeping it simple with just single keys
it might be useful to do diagnostics in real case scenario though
i guess it's easy to rebind though
I have the debug UI thing as an optional plugin (behind a feature flag) in Avian itself, so users can also use it in their apps. There's a resource that lets you control its visibility, so users only need to set up their own logic to toggle it if desired, which is just a few lines
I'm writing the PR description rn so it'll be up Soon™
Objective
Fixes #564.
Expands significantly on #576.
For both benchmarking and optimizing Avian itself, and monitoring physics performance on the user side, it can be very useful to have timing inf...
Would something like ExternalLinearAcceleration and ExternalAngularAcceleration make sense? To easily produce these accelerations externally without having to care about mass?
Yeah I've been considering something like that as part of a force API redesign I've been experimenting with
heron also had Acceleration
It's straighforward enough to implement via a system, but sometimes you just want to set it and forget, kinda like gravity
IIRC my latest design from when I was last playing around with force stuff basically had ConstantForce, ConstantTorque, ConstantLinearAcceleration, and ConstantAngularAcceleration. Additionally, there's a ForceHelper system parameter with methods like
add_constant_forceadd_constant_center_forceadd_constant_torqueadd_constant_linear_accelerationadd_constant_angular_accelerationapply_forceapply_center_forceapply_torqueapply_linear_impulseapply_linear_center_impulseapply_angular_impulseapply_linear_accelerationapply_angular_acceleration- and some more.
The impulses would modify velocity directly, while the non-constant forces and acceleration would be applied toAccumulatedLinearAccelerationandAccumulatedAngularAccelerationcomponents that are cleared every frame
So ConstantForce and ConstantTorque would replace ExternalForce and ExternalTorque, these two new ones would act as the mass-independent versions, and the methods are just so you don't have to care about interacting with them directly? Would persistent go away with the migration to AccumulatedLinearAcceleration?
So "persistent" forces and acceleration would have their own components for efficient querying, while one-off versions are cleared (probably SparseSet components)
How is a non-persistent acceleration different from an impulse though? Is it just scaled by delta time?
Yeah ConstantForce and ConstantTorque would replace the persistent versions of ExternalForce and ExternalTorque. I don't like how the components are sometimes persistent and sometimes not, or how ExternalForce also stores torque for off-center forces. I especially don't like how impulses also optionally support persistence for some reason, that's not a thing in any other engine.
The two new components for acceleration (if we want them) would be mass-independent. ConstantAcceleration::from_xyz(0.0, -9.81, 0.0) would be equivalent to Gravity applied to a single entity. The ForceHelper on the other hand would be primarily for the non-persistent versions and impulses (so we don't need to store impulses anywhere), but the persistent/constant methods are also available for completeness
Impulses should be applied once and immediately. They're not applied over several substeps like forces and e.g. gravity are. The non-persistent acceleration would just mean applying the acceleration over one time step, after which it is cleared
Oh, but it is still applied across substeps, I see
So you could for example apply different gravitational accelerations from nearby planets every frame without manually accumulating and clearing
So if you have an acceleration of (384.0, 0.0, 0.0) without persistance, and the default substeps, it would be first divided by 64 (producing (6.0, 0.0, 0.0)) then divided by 6 (producing (1.0, 0.0, 0.0)) then after applied 6 times in the substeps it would be cleared
My usecase is so mundane 😂 I want those springloaded bathroom stall doors, but I don't want to care about the door mass
So I'm just going to make them fall sideways
just open them by kicking them in
what's that on top of the door?!?! I thought I was the only one who did that
the layerspawn label?
no 👀
Merged the physics diagnostics PR! I've been wanting to work on other things so bad lol, just wanted diagnostics first to make profiling some things easier and more reliable
Oh 😂 It's not poop, it's rusted, it just looks weird without the metalness/pbr shader
Sorry if I'm missing a whole chunk of code/documentation that's already available, but I think the position is that there's not any simple way to bind together a scene that's been loaded into Bevy (from gltf say, complete with rigging) with Avian, is there? I guess what I'm envisaging is a way to import Avian components and Avian joint entities, and to map them to the gltf entities. (I assume there's no way to define Avian components and joints in gltf, is there?) Does that make sense as a goal? But there's nothing out there that can help with that currently?
To be specific, I'm envisaging importing a rigged car model from blender, importing a compatible but not specific avian model from some other file format, importing a mapping file that binds the two together, and then using some sort of plugin to merge them into a Bevy scene, instances of which are easy to spawn? Feasible but not yet on the roadmap? Or am I missing something?
There's ColliderConstructorHierarchy for generating colliders for entire hierarchies (like glTF scenes) at runtime, but nothing for joints. With Blenvy you might be able to set up joints in Blender too, but I haven't tried that
A component that will automatically generate Colliders on its descendants at runtime. The type of the generated collider can be specified using ColliderConstructor. This supports computing the shape dynamically from the mesh, in which case only the descendants with a Mesh will have colliders generated.
You can use most of Avian's components in glTF, that's what me and @janhohenheim did for Crazy Bike for the last Bevy game jam
the map and NPCs etc. were defined in Blender with Blenvy
One limitation is that you can't use Collider directly in Blenvy since it's not Reflect, but you can use ColliderConstructor for constructing it at runtime
eventually when Bevy has an editor, you'd of course be able to add colliders and joints to entities there too, like in Godot and Unity
That jam game was 🔥
how many more deps do avian3d need compared to avian2d?
pretty sure it's the same number of deps as 2D unless parry3d has different sub-dependencies than parry2d
I think it's probably listed on crates.io somewhere
not sure if im seeing it correctly, but my server build has 580 deps. but its probably me who is introducing alot of stuff in addition to avian3d and lightyear. just wanted to know if there was any large difference between 2d and 3d in regards to deps. but not that it matters alot in runtime
thanks, thats actually a useful page for checking deps for a crate
You can also run cargo tree to get a dependency tree for your project iirc
I see avian3d is bringing in the most of them. but its mostly bevy.
Avian uses Bevy with default-features = false unless you bring in e.g. scenes or gizmos through Avian's features for those
I do actually need scenes, so thats probably the one that rises it up
I reduced the number of deps needed to build my project by around 50-60 deps by just enabling features flags so thats sweet. didnt even know that was possible..the minutes building from scratch kept rising as I built. didnt feel good. but thats life of game, physics and networking engines all together.
I'm looking at using joints -
- The docs on the constraint fields are a little unclear - I realized after a few scans that e.g.
FixedJoint::forceis just internal bookkeeping, and not something that I would configure when creating a joint. It would be helpful to clarify exactly when these "state" fields are set. - The
local_anchor1/local_anchor2allow for a custom local translation, but not for a custom rotation. This seems limiting - or is there some way to emulate it for e.g. allowing two entities to have aRevoluteJointto alignentity1.xandentity2.y? Could these be made intoIsometryinstead of justVectormaybe?
I'm trying to recreate mujoco ant robot with avian and bevy to train multiagent swarm robots in bevy. The mode is simple: a body and 4 legs, 2 joints each. However I've immediately stumbled upon a roadblock: once I rotate entities the right way, and apply a joint connections, the rotation is either discarded, or resulting body receives a momentum
Here's how 2 objects are located relatively to each other before joint is spawned Here's what happens after joint spawn pub fn spawn_stub_model( mut commands: Commands, mut app_state: ResMu...
I don't need the most accurate physics like in mujoco, just something that works for a game setting
RevoluteJoint isn't making sense to me. From my understanding, if body B is rotating then it should have no effect over body A, but in the example, it does.
They are supposed to be free to rotate relative to each other
let axle = commands
.spawn((
square_sprite.clone(),
RigidBody::Dynamic,
))
.id();
let wheel = commands
.spawn((
square_sprite,
Transform::from_xyz(0.0, -100.0, 0.0),
RigidBody::Dynamic,
AngularVelocity(1.5),
))
.id();
commands.spawn(
RevoluteJoint::new(axle, wheel)
.with_local_anchor_2(Vector::Y * 100.0)
);
In this example, I'd expect the axle to not rotate at all, while the wheel is free to rotate. That however is not what happens; the axle kind of rotates along with the wheel
Also the anchor points seem to be totally meaningless, as modifying them does nothing.
So I have this cool effect that lets me make my terrain bounce, but the terrain doesn't "move" according to the physics engine. Instead, the ground is a static mesh that is regenerated. I'm not sure how physics engines actually work, but this seems like the kind of thing that would break any guarantees that avian could make regarding objects passing through one another. Like if 2 dynamic objects run into each other at this speed, they'll just stop or slide past each other or something. But since the collider is being generated inside of the player collider, the player will just fall through it. I'm trying to brainstorm the best ways of handling this. The most foolproof way I can think of is treating the mesh below the terrain as a volumetric collider or maybe applying localized impulses based on the effect. I'd love some insight from the certified brain-genius's in this channel tho
I -could- make my character a floating capsule which would help (and I plan to do that) but that still doesn't handle cases where the terrain may be moving very fast. It's a partial fix at best
I think volumetric works, but also checking the new height when the floor changes
Can softness be configured for an individual entity / rigidbody ? It doesn't seem so from what i've seen from reading thru the docs
Hey, I saw your post in another thread (it looks so cool!) but I was wondering how you were handling the physics even in that demo: the player seems to be moving correctly over the top. Are you removing the collider every frame and regenerating a new one when you re-make the mesh? (I'm trying to figure out how to do something similar)
[Edit: oh, just watched your version above... yep, that's what I see in mine when I do the "remove and re-add a collider every frame": sometimes there are kind of "concurrency issues" and my object falls through the floor]
(I worked around this in my prototype by doing a raycast and if I fell through the floor then I manually placed back above. It works for me because it was very simple object and terrain - so might not be appropriate for you)
when would i need object that has rigid body static over just a collider?
i think colliders without rigidbodies don't actually interact with the physics
It seems like you basically worked through this conversation on your own. I have a few thoughts though.
-
Lots of characters controllers use a downward raycast to determine their position. If you start the raycast at (or near) the top of the character instead of starting it at their feet, this would make your character more robust against these types of static-collider-changes
-
You can pair the above with making the collider volumetric. What does that mean? Think quad vs cube. So if your character falls into the ground, they are in a solid collider that will push them up hopefully.
-
You could apply impulses to entities on top of the terrain in relation to the terrains movement.
All of the above solutions are best when the only morphing is in the y-axis. For instance the raycasting helps when the floor moves up and down, but not when the wall moves into you. Volumetric colliders may help but I'm not entirely sure
Has anyone did somthing with joins beyond examples?
Is there a way to give a heightfield some thickness on -y only? I'm using it for terrain generation and I don't want to make things hover over the surface by just using CollisionMargin.
doesn't rigidbody::static get inserted automatically if you have just the collider?
do you think no_std support might happen eventually/soon? getting to that point with my playdate game where im about to integrate physics and have to choose between this and bevy_rapier. bevy_rapier doesn't have no_std support yet but rapier does so im sure its possible
you wouldn't need most of the features anyway so you could try making a simplified version 🤔
I'll probably try both
if i move some Positions around and then do a spatial query, will it just work with the new positions? or do i need to run some intermediate step to update the physics state
The BVH for the spatial queries would need to be updated/rebuilt I'm pretty sure ... If you change positions before physics runs again it would be fine however (since it rebuilds the BVH at the end of a step anyway)
hm. i figured. trying to suss out the laziest way to reverse some positions to do lag compensated hit testing. open to suggestions 🥲
Copy the BVH every frame and put it into a history that holds however many ticks of lag compensation you allow
oh just clone the entire SpatialQueryPipeline?
Yea, if we're gonna be lazy about it, that would be the way to go
and, if i wasn't going to be lazy? sounds like you have a clever alternative in mind that's more work
Well there is the other approach of rebuilding the BVH by calling the system, then resetting your changes afterwards ... That's more expensive unless it's really rare to run lag compensated hit detection however 🤔
well i won’t worry about it until it’s a performance problem, then. should be good for now. thanks!
This is so cool! Any chance that you're thinking about turning this into a full-on lib? I'd love to play around with putting a boat or something on top of a volume like this 🙂
Not sure. It's still very hacky/basic (doesn't handle torque from objects hitting the water “sideways” so something irregularly shaped like a boat might decide to float vertically). I'm currently working around that by segmenting the shape into multiple smaller objects joined by fixed joints, but that probably is too brittle for a fully baked library. My math/physics knowledge is somewhat limited, so I'm not sure how to do the more complicated/correct handling either
Since on the current form it's a single plugin and relatively simple to drop-in, maybe it's better to keep it as a gist that people can copy/paste from, and adapt to their specific needs
That makes sense! Still cool to see someone playing around with it, and I might spend some time doing the same at some point 🙂
this has a simple but reasonably accurate and fast approach
https://youtu.be/-4cphhvxI8g?si=Mgw5EO8CPmn_nL8w
visit https://brilliant.org/b2studios/ for a free 30-day trial and 20% off an annual subscription!
I think the best question to ask is how did someone manage to crash a ship that badly?
Discord: https://discord.gg/KgMgeQ7EMP
Reddit: https://www.reddit.com/r/b2studios/
Twitch: https://www.twitch.tv/b2studios
Patreon: https://www.patreon.com/b2s...
TLDR: Estimate the submerged volume by computing the total volume of the shape, uniformly sampling some number of points inside the shape, and testing which percentage of points is below the water surface. The center of mass to apply the bouyancy force to can also be computed based on these submerged points.
There are other likely more efficient and accurate approaches too, but they're a bit more complicated. I know at least Jolt has buoyancy built in
@vestal minnow what do you think #1124043933886976171 message
When the Bevy 0.16 RC is released I'll probably try if I can get no_std support working, currently Avian is using 0.15 so most of the no_std stuff is not there yet
Last I checked, Parry was technically mostly no_std but there might've been some part that wasn't? Not 100% sure, it might've gotten fixed
If Parry and Bevy are sufficiently no_std then I don't see any inherent blockers for Avian
Rapier doesn't though afaik
https://github.com/dimforge/rapier/issues/227
oh weird i wonder where i got that idea
@fleet mason what exactly do you need from physics? maybe you could get away with just the collision lib?
just a simple physics-based-ish platformer
you're probably right that i could get away with it for the most part but it would limit some ideas that ive had (grapple hook-like mechanic which would require a distance joint or similar)
i'm just assuming that starting with no_std collision lib would be more manageable
doesn't mean that you have to stop there
true, but at some point it might just be easier to help port avian haha
In my case I'm getting the deepest penetration of the collision, dividing by collider aabb size, calculating the length of that, and limiting it to 1.0 to estimate the submersion percentage. It's definitely less accurate than this point sampling approach
Parry supports no_std with now a ci check
Cool, good to know 🙂
is there big_space support of any kind?
If you search the server with
in: ecosystem-crates big_space
it will show a bunch of results from this thread
Huh, TIL that Rapier doesn't seem to have even inter-island parallelism? There's a ton of code for parallel islands and even graph coloring but it's all commented out and WIP, so the whole constraint solver is currently single-threaded as far as I can tell
Edit: It does have inter-island parallelism, but not intra-island parallelism. The commented out ParallelIslandSolver is unrelated to inter-island parallelism.
I was under the impression that Rapier had at least inter-island parallelism, and SIMD constraints, but it's just SIMD
though I'm not entirely sure how that works either, since afaik implementing SIMD constraints properly requires graph coloring, which Rapier doesn't really have
is it possible to change the color of a DistanceJoint and/or ShapeCaster debug arrows?
Should be possible by configuring the relevant properties for PhysicsGizmos as shown here
https://docs.rs/avian3d/0.2.1/avian3d/debug_render/struct.PhysicsGizmos.html
Gizmos used for debug rendering physics. See PhysicsDebugPlugin
But it affects all joints and shape casters, there currently is no per-entity configuration for this
I think this got missed, is there any way to to add collision thickness to a heightfield on -y only?
Parry doesn't support this, so not really
I think implementing a collision shape for this wouldn't be too bad if you could assume that there are no holes, but heightfields support holes which makes things kind of weird
heightfield without holes is reasonable though
heightfield with holes can just be renamed to heightfield_with_holes 😅
yeah but it kind of needs to be handled on the Parry side regardless
unless you
parry
I can definitely assume there are no holes since it's for terrain gen. So I would implement my own collision shape?
do you already have problems with tunneling or is that just a precaution?
I did have a player spawn in a platform and get shot through the floor. I'll have a lot of UGC, so mostly preparing for the inevitable bugs. Players will be able to delete stuff, so I'm not worried about them getting stuck.
Also crashing into the side of a mountain at high speed could cause some issues, and possibly complications with replication.
it's probably fine to leave as is and hope that jondolf will add heightfield without holes soon
i want to use heightfield as a sensor, so that's an extra reason for it to exist
Yeah I don't need it immediately. Just eventually.
Just thought it would be odd to have players float above the surface just so they don't clip underneath.
that's just an early warning of something being wrong with trimeshes though
I guess I could offset the mesh?
i'd say don't do bandaid fixes, the root cause might get fixed eventually
You shouldn't really get tunneling at high speeds, the only case where bodies are expected to clip through things is if they get pushed very hard by some other object (ex: you spawn something heavy overlapping the player and it gets pushed down through the floor)
and that should be very rare unless the object being pushed is very thin or otherwise small, which the player probably isn't
*assuming there aren't bugs
Good to know. I assumed speed would be an issue, I haven't seen any though. Yeah it was an overlapping object with the player, and I'm not certain I'll cover all possible cases when that could happen in the future.
Speed can be a problem without Continuous Collision Detection, but speculative collision (one form of CCD) is enabled by default, which should prevent most cases of tunneling
Hello fine people of avian. How would you recommend I approach building colliders for voxel based terrain ? should I go straight from the mesh (which introduces a GPU readback since I build the meshes on the GPU) or is there a better way to go from a 3d grid of cubes to a collider ?
If they are boxels like in minecraft, just use cuboids, maybe build copound colliders per chunk made up of the largest cubes you can create
If they are smooth voxels you're in for some real trouble unless you have the resources to run convex decomposition for chunks 😅
I think since my voxels are pretty small I can get away with cube voxels and doing the smoothing on the character controller side
I'm trying to simulate a cube dropping on a mesh, then resetting the physics components and letting the cube drop again. I'm resetting the transform, position, rotation, linear velocity and angular velocity components but the cube ends up in a different position after each reset. Destroying and respawning the cube works as expected with the cube always falling into the same place. Is there a correct way to reset the physics on an entity? Am I missing some components?
I think avian physics use custom Position and Rotation components which are supposed to follow the Transform for the physics stuff, which might not reset properly somehow ?
You'd need to also reset or disable sleeping and potentially do something about warm starting values 🤔
That said I've seen Jondolf use examples with this behavior, so I'd imagine the examples have the right implementation for this
Ah true. Couldn't find an example but I'll try to figure out something
The way I've done this in my own examples is by just respawning all the physics entities, this is actually kind of done in the determinism_2d example
Resetting values can probably also work, but contact warm starting impulses (used for making the solver converge faster) might carry over from the previous frame, which could affect things
@south nacelle You could probably try this to prevent contact impulses from the previous frame from carrying over to the current frame, just to see if that's the problem
app.insert_resource(NarrowPhaseConfig {
match_contacts: false,
..default()
});
(also hi :D)
I'll try to repro this myself too actually
yeah the reason I ended up testing this is that I'm building a multiplayer game where for server reconciliation I need to move the player basically back in time and then simulate some amount of frames manually with the physics schedule to hopefully end up in the same position. Currently it's not working properly and I isolated it to the physics system with this cube test. If there are better ways to solve this for that specific use case that would work as well.
@cinder summit would probably know more about that area
but specifically for this warm starting problem you can either disable it entirely with match_contacts: false like above, or I guess technically you could also manually iterate through contact data in the Collisions resource and set all the contact impulses to zero right before you reset the body state. If you're resetting very frequently then the former is better
With Lightyear I believe Avian does currently have some issues with rollbacks though. Afaik it's related to the entity order between the server and client not necessarily being the same, which can cause slightly different behavior on the Avian side. Not 100% sure though
Thanks I'll try the Collisions resource
I didn't have these issue with 0.14 at least, the only issue I have atm is that Collisions is really expensive to clone for some reason and that Sleeping does not get along with rollback at all in any way no matter what I do
Hopefully simulation islands will fix that without introducing major new problems 🤔
It doesn't really make sense for Sleeping to influence the simulation in the first place I guess, so it's undesirable for more than just rollback
uhh they're coming... Soon™ I swear
right now I'm working on finishing a solver optimization that cuts total physics step time in half for some collision-heavy cases
from 16 ms to 8 ms for my current 2D test scene
it also optimizes position and velocity integration
after that the narrow phase is actually the bigger bottleneck, but I'm hoping the contact graph stuff will help cut that down a bit
How well does avian handle rollbacks?
am i stupid?
trying to raycast from my camera
but no matter what i do it only returns the same entity or no hit
ive excluded the player
camera is a child of the player/collider
Mentioned it above, as long as you disable SleepingPlugin and roll back Collisions (or disable warm starting) it should work almost flawlessly provided you roll back all relevant components that might change (position, rotation, linear velocity, angular velocity, and any properties your simulation might change) ... If you need to to be perfectly deterministic (for games with deterministic replication) there might be some extra small edgecases here and there however 🤔
You're casting from the player's position using the camera's rotation? That seems wrong ... Where is the player's origin and how is the camera placed relative to it?
Camera is child of player
So there are multiple possible issues here:
- The camera has no rotation or only partial rotation, and you're not using GlobalTransform nor adding up both rotations
- The player's origin is on the ground, and you always detect the ground
- The camera is offset from the player, and because you don't start from the camera's position you thus don't see things in the center of the screen but offset as if the camera was on the player
I wonder if the solver optimization stuff I'm doing affects rollbacks in any way 🤔 probably not?
Basically I'm just maintaining a SolverBodies resource with a SolverBody for each awake dynamic and kinematic body. A SolverBody stores the velocity and delta isometry needed by the solver, which removes the need for querying the ECS in performance-critical parts. Solver bodies are prepared before the substepping loop and written back to rigid body components after it, so I would imagine it's probably not something you need to roll back
But the SolverBody entries (not their values) and some indices and whatnot are persisted across time steps
Isn't querying the ECS in performance critical parts only a problem because queries don't cache stuff propperly? 🤔
Query::get_unchecked was taking like 2.3 ms in a test of mine while just accessing an array was 0.4-0.6 ms IIRC
If the indices have some sort of impact then rolling it back could result in non-deterministic behavior ... The simple case to figure out if rollback would have an impact is: If a wrong series of events happens, for example things spawning in a different order, or some entity missing for a while, and then get fixed later, does that result in a different output from immediately getting the correct series of events?
Yea the cost isn't actually in the checks I think, it's in having to get the info about where fields are placed in archetypes for every call
Also we'll definitely need SolverBody-like stuff for AoSoA SIMD once we do that
That data should be cached and thus be fetchable quickly using an archetype id
The SolverBody is also exactly 32 bytes in 2D which I imagine is nice for memory locality
If that's after alignment I'm sure the cache would like that yes 😂
and it will fit nicely in 256-bit AVX vectors eventually
(I may or may not have stolen the current design I'm using from Box2D)
If only SIMD at the ECS level 
in terms of its implementation, I kinda like to imagine Avian as striving to be "Box2D, but also 3D, and in Rust, with an ECS"
Box2D is so niiicee
Set the goal even higher ... Box2D but better 👀
Better, and 3D and SDF collisions 😂
Idk if we can beat it in raw performance, but we can of course have more game dev centric features built in (since Avian is for a specific game engine) and the ECS allows for some nice APIs and modularity
Is Box2D really that performant? I thought only bepu had unusual performance 🤔
Box2D V3 is somewhat inspired by Bepu's design
graph coloring + wide SIMD + very data-oriented
I just found out about bepu physics from this chat, why is it so fast? I'm curious now
any links?
C# crimes
It's not really any one thing that makes it fast, but more that the dev seems obsessed with performance
very, very heavily vectorized
There are a lot of ways most physics engines could be improved, but doing so takes time and most physics engine devs just don't give a shit
"It can simulate 1000 awake entities and have unlimited sleeping entities, good enough"
the demo video for bepu looks so cool though
(bepu or bevy?)
oops
Meanwhile bepu physics be like: "What if we have many thousands of awake physics bodies and it also runs fine?"
I mean ... It's a neat tech demo, but I think jondolf's examples look better 😂
oh for sure, but the scale of the demo is so cool
makes me think about what kind of game you could make with fast, large-scale phyics
Like the bepu demo scenes honestly look hideous, if they didn't distract you with their scale it would be underwhelming
we just need to get the atmosphere stuff and raytracing in Bevy and we'll have physics demos that look like all those research papers
like this is so unnecessarily fancy 😂
(from the VBD demo)
This is completely unrelated and has sort of been brought up before, but I've been thinking lately that at some point it'd be worth experimenting with a crate like bevy_physics_common that just provides a shared, physics engine agnostic API. We could then add integrations for Rapier, Avian, or even e.g. Box2D, all using the same components and resources wherever possible, and only diverging for heavily engine-dependent features.
I feel like something like this could be desirable for an eventual bevy_physics; provide a first-party API and an optional official physics integration, but also allow the use of other physics engines without completely fragmenting the ecosystem
I used to be pretty skeptical of this since I felt like engines vary too much, but I feel like it should technically be viable to share like 95% of the API surface 🤔
The main problem is probably different collider types, but we could technically have something similar to ColliderConstructor for the shared type if we wanted to, plus plugins can largely be collider-agnostic like we do with the AnyCollider trait already
Im using avian2d, and I've created a triangle collider with a height of about 100 units. I've made it a dynamic rigid body, and with the default settings on the plugins, it falls extremely slowly
If I change the length unit to 100.0, it doesn't move at all
im thinking i need to define my own mass?
the Gravity resource defaults to -9.81, you can scale that
ah okay
And documentation even says I should:
The default is an acceleration of 9.81 m/s^2 pointing down, which is approximate to the gravitational acceleration near Earth's surface. Note that if you are using pixels as length units in 2D, this gravity will be tiny. You should modify the gravity to fit your application.
thanks!
Shouldn't the length unit being set to 100 also fix the problem though?
A units-per-meter scaling factor that adjusts the engine’s internal properties to the scale of the world.
oh so its just for numerical stability
Yeah basically
cheers!
It's essentially an internal scaling factor to make sure that internal thresholds and tolerances match the scale of your world. For example, how fast can penetrating objects be pushed apart, how large contact tolerances should be, what speed is considered fast enough for restitution to be applied, etc.
so I suppose it's more for simulation stability than numerical stability
bleh... I think I need to do the joint rework before I can land this SolverBody thing, since the current XPBD setup for joints isn't really compatible with it, at least without more nasty hacks and accumulating tech dept :/
Is it finally time to fully
XPBD 👀
I guess back to fighting with hinge joints I go
I've been stuck with a bug with them for ages, I have some custom matrix types that I'm writing unit tests for just to eliminate any potential errors in the math
I've gone over the code like a bajillion times
-# If Avian was 2D-only, this would be so much easier
3D joints are a pain
Get quaternioned
this doesn't even use quats much, it's just a ton of confusing matrix math and dealing with Jacobians
I should write some blog post or guide on joints if I ever manage to finish this, there really aren't any good resources for actually implementing them aside from existing implementations and math-heavy academic papers
if your joints are giving you pain, you should see a doctor 😅
oh my god is the problem really just that Glam's any_orthonormal_pair builds the basis slightly differently than the joint implementation I'm using as reference
I don't even want to know how much time I've wasted on this 
welp the hinge seems to work now, though I still need to implement 3D limits and a motor for it 🙃
call norbo and catto into the bike shed to make it 100%
I'm using tnua for the player,
Applying x rotation to camera and y rotation to player
The origin according to the prints is 1.5 above ground (can see in screenshot)
So I'm guessing its the rotation issue? Do I need to build a dir3 from the combined quat of the player and camera?
I tried GlobalTransform but got type errors on the cast fn
what errors?
globaltransform.translation() is probably what should be used here
that solves type issue but the same hit behavior remains
You'd need both the camera's GlobalTransform translation and rotation for the cast I think
Tho ideally you'd avoid GlobalTransform because it's kind of clunky unless you really only need things to run after PostUpdate and not change transforms again
i must be stupid cause i cant find anything to make a dir3 from a quat
do need to like mul the translation with the rotation?
OK this seems to work. ungodly
Use Vec3::NEG_Z instead of that weird * -1.0 hack 😂
Also you can just rotation * vec instead of explicitly calling mul_vec3 I think
Yea I made that change atleast
Now I'm begging for vec3 to have a Into impl for dir3 so I'm not having to do Dir3::new_unchecked()
You can't turn a vec3 into a dir3 blindly because that's a fallible operation
can't you just do camera_transform.forward()?
Tried but it wasn't giving me results that made sense
Might try again later
I gotta go check out some land for sale
Could at least have a fn on vec3 so we can chain it?
pretty sure there is .try_into()
swing limit seems to work 👀
my current hinge is just a separate point-to-point constraint and angular hinge constraint though, not a combined one since that's uhh... pretty broken still
question: why aren't Colliders Assets?
they can be quite complex, and you can potentially have a lot of them, similar in nature to Mesh
Mainly we just haven't tried it yet. It would also add an extra asset lookup to every place that queries for colliders, so we need to profile if it'd just have more overhead
Colliders store a SharedShape though which is an Arc<dyn Shape> so it's shared anyway if you clone it
Enums have the problem that their size is determined by the largest variant, so even a Collider::Sphere(0.5) would be large in size if some variant stores a TriMesh. But we could instead do something where large shapes have their own asset types, and the enum just stores handles to those, while small primitive shapes are stored in the enum directly
sounds good.
Would make a lot of sense to do that ... Alternatively you could do something similar to what I did for my SDF trees, where I separated the data and structure so the enums don't grow even if I add something with dozens of fields
wait, surely the TriMesh variant would have a Vec in it? The enum's size wouldn't be affected.
a TriMesh stores something like at least 14 Vecs internally
it has a Qbvh and a bunch of topology data and pseudonormals etc.
Do you plan to do benchmarks for speed comparison between different engines?
Not right now, but locally I do have some of Box2D's benchmarks and samples implemented with Avian, like tumbler and large pyramid
I'm mainly profiling Avian against itself for now since there are so many large optimizations we need to do still
once we do have a lot of the big optimizations done though (solver bodies, simulation islands, better contact management, graph coloring, SIMD contacts, BVH broad phase) it'll be interesting to see where we'll be relative to other engines
Pure curiosity but do you have an general idea of where Avian stands performance wise with other engines ? Mostly curious about mainstreams ones like box2d or whatever is used in the big engines
currently it's definitely slower than Box2D, Jolt, and Bepu, and also slower than Rapier in a lot of cases, but a lot of it depends heavily on the scene
some people have said it's faster than bevy_rapier for their projects
but for large scenes with lots of contacts and/or joints I would be very surprised if it is
most projects also just aren't that intensive on physics, like it's relatively rare to have tens of thousands of dynamic bodies being simulated and colliding at once
so physics perf is unlikely to be too big of a concern for a lot of projects
Out of curiosity, how does 3d avian perform compared to 2d avian for the same scenario (for example same number of spheres vs circles)?
Should be pretty comparable if the number of contact points is roughly the same. But there is some inherent extra cost in 3D because of the added dimension, for example rotation uses quaternions instead of just a complex number, and angular inertia is a 3x3 matrix instead of a scalar value. Friction also becomes a bit more expensive since it now needs to consider two tangent directions instead of one
Currently 3D also has gyroscopic torque by default, which adds a bit of cost to velocity integration, but we could make it opt-in and otherwise optimize it a bit
A fun optimization I discovered in Rapier a couple of days ago is that we could also precompute velocity increments caused by external forces and torques at the start of the time step, and apply them at each substep with just a single addition. For 3D though, the changing orientation of the body should affect the angular inertia and gyroscopic torque, so you'd lose some accuracy there...
@vestal minnow in picking example Shape marker isn't used 🫠
and idk what i'm doing wrong but require_markers: true and PhysicsPickable doesn't work
oh, camera needed it too, derp
Hi! Im trying to impl this ytb video using avian.
https://www.youtube.com/watch?v=CdPYlj5uZeI
The experience and result are pretty good!
but it always jittering, and i cant find the reason. pls help me~
A detailed look at how we made our custom raycast-based car physics in Unity for our game Very Very Valet - available for Nintendo Switch, PS5, and Steam.
BUY NOW!! https://toyful.games/vvv-buy
~ More from Toyful Games ~
- Physics Based Character Controller in Unity: https://youtu.be/qdskE8PJy6Q
- Instant "Game Feel" Tutorial: https://youtu.be/...
here is my code~:https://github.com/LTstrange/car_race
Also, when the speed is high, it seems like the ray caster is lagging behind.
This happens especially when moving at high speeds or high rotation, and it feels like the ray caster can't keep up with the movement
To me it looks like the kind of thing that would happen if you were applying forces in a frame dependent way, so that variations in FPS would make the car "jitter".
Could be something else though, just a hunch
Is it possible that is just the debug visualizer that is lagging behind? And that maybe the actual raycast isn't? Not sure how you figured it out just guessing
Also I'm assuming you're doing this in FixedUpdate, so there will be some visual jitter. You have to interpolate the visuals.
But I think @vestal minnow has a solution for this with Avian?, I just dont remember what it is
Ah there ya go, thanks!
hmmm, I dont think thats the reason, because Im doing this in FIxedUpdate. but... is it possible that because I set the ExternalForce with_persistence(false)?
I also think about that, but its really hard to check.
one thing I ran into was using really small, light objects which made things jittery
Thanks! I will give it a try~
It is! when the car ColiderDensity was default(1.0), its way jitter than set to 5.0
And the force is constantly and rapidly changing
I've noticed that the jitter occurs very regularly, happening about once every 0.2 seconds. It happens regardless of the speed; it just becomes more noticeable when the speed is faster.
Sh*t! I've figured it out! By placing the camera update function in the FixedUpdate, the jitter is gone!
However, I tried using TransformInterpolation, which not only failed to solve the problem but also increased the jitte, and the pattern remained the same as before.
I suspect that whether RayCaster or PhysicsDebugPlugin has interpolation enabled by default, which might be causing the jitter. However, this still needs to be verified.
everyone talks about jitter and interpolation, but i've never had a problem; is 144hz fixedupdate too high for smaller computers?
i think the default setting is 64hz fixed update.
Make sure you put the camera back to update when trying the interpolation
And you probably want to use linear not extrapolate
yes I did, that's exactly what I did.
and since Im using mac, the refresh rate is 60hz, and fixed update is 64hz. the interpolation may not needed.
Hmm then I don't know sadly. I've never used the interpolation built into Avian yet either to really know for sure. But it sounds like maybe you're seeing more than the jitter I'm thinking about
this will still have a bit of jitter without interpolation, since some frames will have more than one physics frame
afaik the only way to not have jitter without interpolation is to have the fixed update rate be a integer multiple of the refresh rate (or a integer division)
I can't tell from your video what you mean by jitter, but one thing I ran into was when I made my camera smoothly follow, there would be a little bit of jitter and the reason was the camera updated more often than the physics ran and so it would move while the world hadn't changed and then adjust again on the next update
maybe i need to find a better way to record, current one's quality isn't good enough.
yeah, I ran into that a lot too
maybe this one is better? you can notice the gizmos is flickering( some kind of back and forth)
the raycaster gizmos, four tire
and this one in high speed (more obvious), start about 0:10
this is what happened when camera in update, and enable interpolation: first the jittering is more obvious, and second, the mesh(faint blue) didn't match the collider gizmos🥲
Sorry for taking up so much time in this channel😅 I really appreciate everyone's enthusiasm and help!
Its ok! Thank you so much for your help! 😋 Have a great day~
i have 1 system that has 0 lag or jitter, and another one that has an exact same schedule but lags and jitters. i have no idea what affects the outcome but it's consistent, so i figured i'll move on to more important stuff since i know that it's possible, so i can figure it out later
yea, maybe i should move on too. Devil hidden in details, we cant look into details too much, but doing nothing.
i kinda like jitter in your case because it looks like vibration from the engine tbh
yes! for sure! i like it too, but not under control made me sad about it
i think in different setting, like 120 hz refreash rate, the jittering may disappear, that would be bad..
How are you enabling interpolation? You probably want it for the bodies, but not necessarily for the camera, you can just update the camera's transform in Update like normal
I insert the TransformInterpolation component in car body, and put camera in update,
the camera is tracking the car's position, move to target pos(behind the car), and look at it.
Hmm yeah that sounds correct
Something you could try to exaggerate the problem is to set the fixed timestep to some very low tick rate, e.g. .insert_resource(Time::<Fixed>::from_hz(10.0)). If interpolation is working, it should still look relatively smooth, but if not, then something is most likely going wrong there
this is 20hz result, camera in update, with interpolation.(10hz is super unstable)
it's not just the debug lines that are doing that right? have you confirmed that? It looks like it's everything, but may be tricking the eyes
Because the shadow looks smooth to me
this is 20hz, camera in fixed update, without interpolation. which is way smoother.
I'd honestly try disabling the debug output for a sec
because if the body is jittering you'd expect the shadow to be too
the mesh movement is smooth, with camera in fixed update, without interpolation. and the gizmos is also smooth.
ya that makes sense but you want camera in Update otherwise it wouldn't be smooth on a faster monitor
as far as i notice, the camera in fixed update will make sure the movement is smooth. and interpolation will make sure the mesh didnt catch up the gizmos.
I just wonder if it's a bug with the gizmos and otherwise the physics are working fine
hmm, i will try disable the gizmos, camera in update, and with ot without interpolation
that will be jittery
I would try disabling gizmos, camera in update, and then interpolation on
Yeah I think the gizmos use physics positions, not interpolated positions, so they might not be as smooth
ohh ! it work! no more jittering😂
yeah thats what I figured, the shadow gave it away 😛
that really make sense
thank you very much!
i cant believe i havent try that before
I guess we could make the debug rendering use interpolated transforms too, but it technically wouldn't match the actual state of physics perfectly 🤔
could be an option, both are useful
yeah
something is kind of bothering me. CollisionLayers and CollidingEntities are in the same entity as the collider and not the rigidbody. That is annoying to me but not hard to solve. But the thing is, RigidBodyDisabled doesn't disable the children colliders? (or it is disabled but the colliding entities still updated, i haven't test much yet)
though it'd make queries annoying
but the gizmos seems lagging behind in very fast speed, that may be a problem🤔
i mean the parent gizmos(collider) and raycaster(tire, children of the body) didnt match
RigidBodyDisabled currently doesn't prevent collisions from being detected, it just disables physics from acting on the body (i.e. velocity, forces, collisions, and joints do nothing). For disabling colliders, you can use ColliderDisabled. But they both only affect the entity that has the component, not children
I believe bevy_tnua has something like this iirc
yeah seems like there's this
https://github.com/idanarye/bevy-tnua/blob/main/physics-integration-layer/src/data_for_backends.rs
just for Tnua's purposes though
i am currently making a kind of bullet pool, and the bullet has collider as a child instead of the collider same place as the rigidbody. I guess with this system it's a lot better to place the collider the same as the rigidbody
but is that the intended behavior or it's just haven't implemented yet?
I think it's desirable to support disabling a specific collider, not just all attached colliders. But the latter would also be nice to support, so that you can just disable all collisions for a rigid body without having to deal with children. So some things might change or be expanded
I'm definitely open to improvements there
It'd be nice if there was some form of first-party "component propagation" in Bevy so that you could add a component for a parent and it'd optionally affect descendants too
there's an open PR for something like that already
good to know
right now i will change my implementation so i can easily work with avian
pool as in OOP pooling?
otherwise i don't get what you're doing
wait, but then should i insert both RigidBodyDisabled and ColliderDisabled for disabling a body?
actually though im not sure if im doing it right in ecs pattern
i just want to NOT frequently spawn and despawn bullets since my game will have A LOT of bullets
or im just doing premature optimization
no idea, it's worth benchmarking but even if it's an optimization it's definitely a premature one 😅
ECS pattern is very rare in the wild so things like this is hard to know
also compared to usual oop object pooling, the one i have implemented is NOT pretty
made me question my life choices
i actually wonder if pooling makes any sense in unity now that it switched to ecs 🤔
switched? isn't it just became a package?
idk, i remember reading something about unity switching to ECS on the background without changing anything for users
I think that's next version that's years away 🤔
it's not too different from what they already had, if you just imagine that script component is a marker component
just more of a pain to work with, and less flexible compared to bevy ecs
In most cases yes ... Might be some slight exceptions if your entities have components that require some allocs, but when I tested this a couple of bevy releases ago, spawning new entities constantly lets you spawn hunderds of thousands of bullets per second, which should be enough for basically every genre I hope 😂
good to know 😂
Image if i hadn't say this here, how deep would i go
i'd be more concerned about avian's performance than spawning
might be better to use plain distance checks
for bullets?
yeah
bullets mostly have constant speed so you can store just the direction for them and move transform
and check only the player
are you saying there are better physics engine for it like rapier or for general thousands of bullets is not really good with these physics engine?
Thousands of bullets as physics engines usually doesn't end too well
Tho if Jondolf is to be believed Box2D and bepu would handle it like it's nothing 😂
either way it's like 10 mins work to do the bullet movement + collision check without physics
if you have environment to collide with then that's another thing though
I mean a few thousand shouldn't be bad
make sense, since my bullet will likely have simple geometric shapes
i agree with this though
The one time someone did 1500 projectiles as rigid body entities it started hitting the limit tho 🤔
for the bullet hell games that i played only player bullets needed physics though, and 99% of bullets weren't from player
it will have interactions with environment, but the shapes are simple to do simple collision check, but my bullet probably won't go past 3000...
I wonder how many projectiles my spherecast-powered mess can handle ... Probably not actually as much as just doing projectile collisions using intersection tests against capsules in default avian, then storting the result by distance 
if you mean that old one by RJ, it was 15,000 projectiles, and in bevy_xpbd
Wait it was 15k? 👀
yeah
No Audio. Running in local sandbox mode, so no networking or rollback.
disabled bullet expiry, curious when it would lag.. each bullet is a small rigidbody circle collider. went up to 15,000 or so. you can see the entity counter in the bottom right. I've not bothered profiling this scenario, but something in physics systems is hard at work.
N...
Yea that's the one I was thinking about
By all the numbers I got avian should be faster too 👀
I'm trying to find that newer one with a ton of asteroids breaking into pieces and stuff
thats crazy!
then i think it's still fine for me to use the engine for my probably not pass 3000 bullets game 👀
so the bullets can even collide with each other?
this isn't that one but the second video in this post has over 30k bodies (but not actively colliding it seems) at 120 fps
#crates message
(I'm assuming it's using Avian at least)
yeah
im totally shocked
it's not even that good tbh, it should be possible to make it much faster I think
collisions are the main thing that is costly for physics, and in these demos there aren't any crazy amounts of active contacts
physics engine is magic to me.....
Yea iirc the bottleneck was actually the broadphase, which I guess makes sense considering how uh ... primitive ... it is
rely on a physic engine but not to rebuild the wheel should be the smartest thing for me..
Pretty sure it is for most people
There's not even really much reason to make a physics engine from scratch now that avian exists either ... Want something fancy? Just build it using avian 😂
meanwhile Nise implementing custom SDF collisions
Hey, I didn't rebuild any wheels, no bevy crates ship with SDF collisions okay 😂
After I figure out OBVHS for my raymarcher I should probably look at updating the SDF collisions for my updated SDF tree stuff 🤔
after seeing pezzza's verlet engine ytb video, i thought, oh that interesting, i should build one. (but never pass 3k objects)😅
i tried everything, it just didnt work
iirc his video on Verlet physics was actually one of the things that originally inspired me to want to try implementing my own physics as well
among some other videos and articles I found
I was in a Verlet physics rabbit hole at one point and partially found XPBD through it if I'm remembering right
(and Johan Helsing's blog post on implementing 2D XPBD for Bevy)
looks a little bit similar to me, actually🤔 both by manipulating position? some kind of ?
Can't even with full confidence recall how you got to this point 😂
same 😂
At least I can still remember how I ended up doing SDF collisions 🤔
me, right now👀
XPBD derives velocity based on the change in position from the previous step, which is similar to position-based Verlet approaches
Honestly still feels crazy how avian used to be this small obscure library, and now basically half of the bevy ecosystem uses it 😂
i think the github readme explain this very well. I also tried rapier, but avian's doc is much better, and very easy to use.
That's basically the goal afaik
Legend has it Jondolf actually likes writing docs ... somehow 
you could see that it has more potential than rapier even in 0.1 though
or was that just me
I was keeping an eye on the bevy_xpbd repo before it even had a release iirc 😂
rapier just had too many bugs and I wasn't able to understand the code to the point where I felt I stood any chance fixing them
hmm, doesnt rapier "battle tested"? cant remember where i saw that.
it is, just not by games lmao
althought i didnt try rapier deeply, but just have that feeling
Yea most problems with rapier are actually kind of gamey problems
Rapier is more mature and has been around longer (its predecessor NPhysics is also nearly ten years old now), and it currently has more physics features and optimizations and stuff
also has more and bigger sponsors (like Foresight)
Worth noting that half the optimizations are actually commented out tho 😂
yeah lol
Rapier (or specifically Dimforge) also seems to focus a bit more on robotics and industrial applications recently, while I'm focusing more on specifically game physics
though they're not necessarily exclusive
is it possible that sometime avian will be built into bevy...
There's no reason Bevy doesn't have its own physics engine, while avian is an option
It's not a very high priority right now, but Bevy will most likely have some first-party physics eventually
this could use Avian, but it's definitely not ready for it yet (and neither is bevy_rapier imo)
I imagine the physics engine would also be easily swappable still, so you could opt in to using other engines if you wanted to
bevy philosophy
removing those object pool codes feels sooo good
congrats!
honestly looking at some of Rapier's code it feels like it shouldn't be too hard to beat it in perf once we have a few more of the big optimizations implemented 
from what I can tell, there's very little solver parallelism (since all of that is commented out), and there's a lot of things that feel kind of sus
Also am I misremembering or is Rapier non-deterministic with SIMD enabled? I don't see why it would be, but if it is, that feels bad
imo you should keep determinism even with high parallelism and wide SIMD, it's definitely doable with the right architecture
It's probably mostly a matter of actually taking it into account
For avian it makes sense to care about determinism because people ask you about it quite often, but for engines that are developed almost without any interaction with gamedevs it's not surprising they ignore it for all the problems it brings
removing ~~ those object pool ~~ code
sfeels sooo good
I mean this is true, but object pooling is just the worst, so removing it must then also be better than average 🤔
Is it possible to make physics debug rendering opt-in per entity? Looking at the source it seems like it's everything with the option to disable specific entities?
Also doesn't seem like there's a way to toggle it at runtime?
i was talking about the ecs one tho, is the oop one also bad?
in OOP it's an unfortunate necessity 😅
make sense
Is it possible to make physics debug rendering opt-in per entity?
You can configure debug rendering to not render anything by default
app.insert_gizmo_config(PhysicsGizmos::none(), GizmoConfig::default())
and then you should be able to add the DebugRender component to opt in for individual entities
Also doesn't seem like there's a way to toggle it at runtime?
You can do this with theGizmoConfigforPhysicsGizmos, same as other gizmo groups
fn toggle_debug_rendering(mut gizmo_configs: ResMut<GizmoConfigStore>) {
let (mut config, _) = gizmo_configs.config_mut::<PhysicsGizmos>();
config.enabled = todo!("toggle this");
}
Awesome, thanks!
@vestal minnow I’m currently looking into continuous rollbacks issues in the lightyear crate when using avian.
Is there still a source of non-determinism you are aware of in avian? (Like Order of contact pairs or similar)?
(Not talking about cross platform determinism, but rather same platform/machine determinism)
I want to rotate a point about another point (or the origin, I can transform to and from that if need be) by some number of radians
having some trouble figure out how
I want to rotate it depending on the contents of something's AngularVelocity
nevermind, I do believe I have figured it out
afaik Entity ID order affects behavior, so if entities between server and client are different, it could desync
(mostly for collisions)
Could this be modified? Could you point me to the system which this happens?
Even if it would be problematic for performance (like reordering to have them sorted by another criteria) would be fine as I want to fist find out if that’s the source of the rollbacks.
I'm not sure where the problem is, the order of entities inside contact data shouldn't be at least directly dependent on the Entity ID
the entities for the keys of the IndexMap in Collisions are sorted by entity, but that shouldn't affect things unless the sim uses those entities stored in the key somewhere
hey @vestal minnow have you also encountered this cargo doc bug? https://github.com/rust-lang/cargo/issues/15203
Yup I have the same problem
need to manually remove the docs folder for avian2d/avian3d every time I rebuild docs (or use cargo clean)
it's rather quick with clean -p but eh, my mental burden 🥲 ; thanks for confirming
I wanted to try another approach of filtering out very small differences in position changes/velocities, to see if its a floating point precision issue.
When I access the Collisions (during PostProcessCollisions) all the total_normal_impulse are 0.
But if I filter out the ones which are have 0 impulse, collisions are not working.
At what point in the schedule can I best prune forces which are below a certain threshold?
you can't, since the impulses are computed in the solver, and applied immediately
filtering by penetration depth and/or impact velocity would be the next closest thing
but I don't see why this would be a floating point issue, std floating point ops used in Avian should be (at least) locally deterministic
the entity order problem can be easily reproduced by running the determinism_2d example with the entity columns shuffled with
let mut cols = (0..COLUMNS).collect::<Vec<u32>>();
cols.shuffle(&mut rand::thread_rng());
for col in cols {
// ...
}
without shuffling, it's perfectly cross-platform deterministic
in both cases the initial transforms should be identical, just the Entity IDs assigned to the bodies are different and they're spawned in a different order
(same problem even without the joints and just collisions)
thank you! That could be it then, as the entity ids, in the different "worlds" (clients) are not the same, so this could be the cause of the divergent simulations.
Now the question is would there be a simple way to make this order independent of the entitiy ids (as I don't think that can be modified in bevy).
The determinism_2d sample panics for me with:
thread 'main' panicked at crates/avian2d/examples/determinism_2d.rs:253:41:
attempt to add with overflow
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Encountered a panic in system `determinism_2d::update_hash`!
yeah that seems to happen on some runs, not often for me though
for me its deterministic (for now)
I think it's just a problem with how the transform hash is computed
fn djb2_hash(mut hash: u32, data: &[u8]) -> u32 {
for &byte in data {
hash = (hash << 5).wrapping_add(hash).wrapping_add(byte as u32);
}
hash
}
ah
this seems to not have it happen up to now?
hmm okay yeah without the joints it is deterministic even with shuffling
for the current joints it makes sense that it's entity dependent
@quartz heart the lightyear demo doesn't use joints right?
seems like it's just rigid bodies and colliders which should be perfectly deterministic, so I'm not sure what's going on if there's still constant rollbacks 🤔
one thing to note is that I think collision events and CollidingEntities have the entities sorted by Entity ID currently, so they may be in a different order across clients. But I don't think the lightyear demo is using that
The object pooling I've seen people make in unity looks pretty crappy too, and the one I once built for godot also sucked (plus to get decent performance in godot I had to build a sort of one-off ECS just for projectiles)
Our construct system utilizes colliding entities, no rollbacks related no
Indeed it’s simply a dynamic body (characters controlled by wasd) no joints for the movement
No it’s simply the velocity is constantly non zero on the client but not on the server (and the position changes slightly every update)
🤔 I feel like my fps starts dropping once I have ~150 of my raycast rigid body cars going (but not colliding)
it'd be cool to get 1000s (or even just 1k) going, but.. I'm guessing applying forces to keep the rigid bodies in the right position is enough to start affecting performance?
omg all your cars are raycast+force simulated ? could you approximate cars far away with a simpler "raycast/shapecast on the ground + lerp rotation to horizontal or normal ?", like a simpler to compute kinematic approach ?
I'd assume they don't need the same precision as your player car (if that's still the same game you shared a few times), gamedev is full of cheats like that in my experience heh
yeah I started experimenting with that and then dropped it when I didn't notice a difference.. but now that you mention it, I should look into it again. I realized at some point that my laptop has a cap on performance unless it's plugged in.. like, within the past month I realized this lol
measure and adapt 🤞 and keep us posted :D
yeah... I thought doing something like that would be helpful.. like cars that are far away, I was disabling their rigid bodies and colliders with that disabled component.. I was going to even have background cars that don't have any physics at all...
still... not seeing a significant difference in the framerate 🤔 gonna run a trace
🤔 but you mentionned raycasts ? wouldn't you have to disable those too ?
yeah if your bottleneck is on rendering those or whatever it's good to know you're looking at the wrong place at least 🙂
the place im currently working as intern also have a game that has bullet pool and it's... well, make you go wtf
well, the game full release was in 2017..
@vestal minnow for me in the determinism_2d sample I get different hashes even without joints:
(while with the same order of entities spawning, I get the exact same hash)
on your machine you get deterministic results?
For the system that computes the hash, I'm locally sorting the positions based on the x coordinate (or y coordinate if x1 == x2) because with shuffled entities the query iteration order will change, which affects the hash
also make sure the joints aren't there since they're not deterministic across different entity orders atm
I think we'll need to partially move them out of the ECS to fix that properly (we probably want to do that for perf reasons anyway though)
You are correct, when sorting the entities by position, the hash is the same.
So its not avian which is the source of inconsistent simulations...
that would actually be a really cool stress-test example @vestal minnow 🤔
I've been trying to sync a Sensor's position with a RigidBody, and it seems to lag behind. I mutate the Sensor's Position in a system after StepSimulation and before Sync. In my mind that should cause them to line up, but the Sensor still lags (according to the Debug Render). Why is that ?
Hello, is there a built-in component to have a collider that generates from a mesh everytime the mesh data is updated or do I have to make it myself ?
I tried using the collider-from-mesh feature with a ColliderConstructor but it doesn't seem to work
How does LinearDamping work? The docs don't specify
Nevermind, I've figure out a solution to my problem that makes this irrelevant
but I have another question
How can I tell whether a change in velocity was because of a collision or something else (eg: gravity)?
I figured it out sorta
but am back at the LinearDamping question
^
basically, I need to use lineardamping manually
outside of the physics system
I'm making a player controller and it's for that
The implementation? It's the Padé approximation of exponential decay
lin_vel.0 *= 1.0 / (1.0 + delta_secs * lin_damping.0);
thanks!
My controller works within the physics sim by subtracting what it set velocity to last tick (velocity needed to stay on point on floor (rotating + moving floors supported) + player in input) from the velocity to see the impact that the sim had on the velocity
and it adds that to the velocity it wants you to move to get what you should be moving at
I'm gonna see if this damping equation works to add damping when I apply it to the part the physics sim changed
since that part persists across physics updates in a way because it's used to calculate the next velocity
then I'll just need collide-and-slide so I don't run into walls, get stopped, and think the sim wants me to move in the opposite direction because the wall set my velocity in a direction to 0 and made the difference the opposite of the intended velocity in that direction
you were right on this, I think while reducing the complexity of the simulated vehicles the further away they are makes sense (and is something I'll eventually do) I think I can definitely get a lot more vehicles running
bump
I would appreciate if anyone could point me in the right direction
There isn't, ColliderConstructor only handles generating the original collider when the mesh is added. There are methods like Collider::trimesh_from_mesh, Collider::convex_decomposition_from_mesh, and Collider::convex_hull_from_mesh for manually creating colliders from meshes, you could use those to update it
Thanks, i'll do that
That would be cool to develop tho
@vestal minnow Hi. I wanted to know, how much of Avian's physics is deterministic. I want to create rocket simulation, and I would want to set parameters by myself, without worrying that some random values would screw the results. So, if I e.g. add force to the rocket, and observe the results multiple time, I would want to see exactly same results every time, if I didn't change the values
I got the collider updating from mesh working, but it seems that once one of the rigidbodies becomes asleep, it doesn't react to the ground under it disappearing.
This might be expected behavior but idk (i'm running this with the default settings).
@vestal minnow and maybe add the answer to FAQ 😅
Collisions, joints, external forces and torques etc. should be cross-platform deterministic. Most determinism caveats are around networking, for example joints are not deterministic if the order of entities is not the same across clients (this will likely be fixed in the future)
For cross-platform determinism you need the enhanced-determinism feature flag, but even without it Avian should still be locally deterministic
Yeah I'd probably consider this to be a bug. For now, you could get rid of sleeping by disabling the SleepingPlugin or adding the SleepingDisabled component to individual entities, or alternatively you could try manually waking up the entities colliding with the mesh when it changes, using the WakeUpBody command
(you'd use the command like commands.queue(WakeUpBody(my_entity)))
First option sounds great but i'm not sure about the syntax
app.add_plugins(PhysicsPlugins::default().build().disable::<SleepingPlugin>())
Thanks a lot
I think my bottleneck right now is related to the outline shader I'm using (and also, just recording video seems to impact things) but I think I can get a good amount of vehicles going at once
figured this was fun to look at
Contribute to komadori/bevy_mod_outline development by creating an account on GitHub.
yes. I'll add that... I'm trying to outline a lot of things at once and haven't bothered trying to optimize that yet
Great. I have few more questions. The idea behind the project is to launch a rocket, and then via the movement of the engine let it land without crashing. Something like SpaceX demonstrated but on much, much lesser scale. When rocket is close to the ground it is meant to just spread "legs" on which it can land(Can me called by hand, doesn't really matter, info just for the physics context).
Which elements of the required physics for this can Avian Physics provide? I know, that is can provide collisions, and I guess it can also provide thrust force. What also relevant (like e.g. air resistance) can it provide?
you'd have to figure out aerodynamics on your own, everything else should be easy
The contact graph and better pair management stuff I'm working on bring some nice perf wins for collision detection, the narrow phase typically seems to be 3x as fast, and the cost of collision reporting (events + CollidingEntities) is now pretty negligible. There are some wins elsewhere too
(ignore the image ugliness lol)
this is without the SolverBody stuff so the solver is really expensive still
there are also more low-hanging fruit to optimize this further, like using Query::get_unchecked more
Hello, I want to raycast from my camera that is inside my player character (as a child).
.with_child((
IndexedCamera::new(0),
FpsCamera::new(0.1),
Transform::from_xyz(0.0, 0.6, 0.0),
RayCaster::new(Vec3::ZERO, -Dir3::Z).with_solidness(false),
));
But even with solidness as false, it reports the hit on the collider it starts inside (white sphere is the origin).
Is this a known issue or am I just doing something wrong ?
yup, use a sphere shapecast or exclude entity
solidness false is like when you're in a room, and you want the room to be a box for some reason
probably could use a better name
ah I understand my mistake, I though it would ignore the collider, but rather it will just hit it's outside edge instead of instantaneously
makes sense thanks
do you know how I would exclude the entity ?
with a query filter ?
Query<(Entity, etc)> if it's unique, yeah
i ended up storing player entity in a resource because it was required too often to bother with constant clashes
jondolf will probably add ignore to raycast soon though, so you might as well just use a spherecast for now
but why would I use a sphere cast ?
also i'm not sure about the query_filter syntax
it's basically same as raycast if you use a small radius
filter is just a simple struct, and it can be const for reusable settings
const config: ShapeCastConfig = ShapeCastConfig {
max_distance: 100.0,
target_distance: 0.0,
compute_contact_on_penetration: false,
ignore_origin_penetration: true // <-- this is what you need
};
@vestal minnow maybe instead of solidness or compute_contact_on_penetration, "invert_collider" would make more sense and be more useful?
Am I doing it wrong or is computing the actual global world position of a Raycaster's hit quite convoluted ?
This doesn't work but it's where i'm at rn:
let pos: Vec3 =
gt.translation() + raycast.origin + gt.forward() * *raycast.direction * hit.distance;
Where gt is global_transform of the raycaster and hit is one of the rayhits
Would be so nice to just have position in hit, but I guess there is a good reason for that
probably because it's simple in cast_ray 😅 i think that was brought up before
Yeah I know, that's what I usually do that's why it's so painful to have to do weird math for such a simple thing
I need to do a raycast every frame so it seems dumb to not use Raycaster
does it actually do any optimizations though? i guess i'll have to check source later
I still have this issue. I even have SyncConfig { position_to_transform: true, transform_to_position: false, ..default() }.
did you try before/after transform propagation?
In Avian or Bevy ?
The system runs before all sync operations. I even disabled transform_to_position so the Entity's Transform should have 0 affect on this. Also, PhysicsDebug uses the Entity avian::Positions iirc, so as long as Transform isn't writing to Position (which it shouldn't be with my SyncConfig), it should align
Just so I'm not leaving misinformation here: @grizzled depot clarified that Rapier does have inter-island parallelism (here) but not intra-island parallelism (meaning parallelism within islands). The commented out ParallelIslandSolver stuff is unrelated to inter-island parallelism.
That matches more what I expected
Hmm that seems strange 🤔 The debug rendering is currently done in PostUpdate by default, so just to make sure it's an Avian problem you could try syncing the position in e.g. Update (which is after any of Avian's physics systems) to see if the lag persists
I just have a very simple example that just creates a RigidBody that movies under Gravity, and a Sensor.
app
.add_systems(
FixedUpdate,
move_sensor.after(PhysicsSet::StepSimulation).before(PhysicsSet::Sync)
)
The Sensor is updated via :
fn move_sensor(mut q: Query<(&mut Position, Has<Follower>, Has<Leader>)>) {
if let Some(&target) = q.iter().find_map(|(p, _, l)| if l {Some(p)} else {None}) {
let mut p = q.iter_mut().find_map(|(p, s, _)| if s {Some(p)} else {None}).unwrap();
p.0 = target.0;
};
}
Avian runs in FixedPostUpdate by default. You're moving the sensor to the position before physics here
o
// TODO: Would batching events be worth it? (from report_contacts)
I'm running into 4-5ms of time spent in report_contacts and am now wondering what I can do to reduce that
lol geez sorry, there is send_batch on event writer. I'm gonna try that out
That seems like an insanely high number :o
How many contacts do you have? I had a scene with 5k+ contacts and it was ~0.2 ms (but I didn't use CollidingEntities there)
uhh, I feel like not a lot but maybe that's highlighting something I'm doing incorrectly
by "feel" i mean "I didn't measure so idk"
Also the cost for contact reporting should be way, way smaller once I make that PR for the contact graph and better contact pair management
(it's on this branch, there's just a weird 3D determinism bug I need to track down and some other small things I need to fix)
I should split out some more things from that branch into smaller PRs lol, it's pretty massive
at least tangent velocity which is completely unrelated
I'm gonna see how many contacts I have
and also maybe check out that branch ||since I don't care about determinism 🫠 ||
also, on the main branch (or that branch) you can add the PhysicsDiagnosticsPlugin and PhysicsDiagnosticsUiPlugin (requires diagnostic_ui feature) to add that fancy overlay I've been using with physics timing information and stuff
ah, got a panic on
debug_assert!(contact_pair.manifolds.is_empty());
curious what that's all about but... running it in release is glorious 🤩
nice!
That's a bug with the current implementation then, it's trying to create a contact pair even though there are no actual contacts yet
I guess it's good that I added that debug_assert :P
should be fixed 🤞
wow with the narrow phase improvements the broad phase is now often more expensive than the narrow phase lol
the broad phase cost should go down once we properly rework it to use OBVHS though
of course we're most severely bottlenecked on the solver though, it's over 50% of total physics step time in my current test scene
that should hopefully be improved quite a lot by the SolverBody stuff and parallel simulation islands in the near-ish future though
and later on graph coloring + wide SIMD for contacts
oh my god 🤦♂️ I was testing with a variable time step at one point for perf reasons and forgot about it... so no there is no determinism bug, if you're running with a fixed time step like normal 🙃
works for me, no more panic 👍
Nvm managed to lower the broad phase overhead a bit with a hash set for faster lookups of existing collision pairs
we are severely solver-bound
Hey, I've had this assertion fail a few times and I can't really trace what I'm doing wrong.
assertion failed: b.min.cmple(b.max).all()
stack backtrace:
[...]
3: avian3d::collision::collider::ColliderAabb::grow
at [...]/avian3d-0.2.1/src/collision/collider/mod.rs:378:9
4: avian3d::collision::collider::backend::update_aabb
It would appear that some AnyCollider::aabb or ::swept_aabb generates a malformed AABB
Possibly the cylinder shape. I made a cylinder collider into a cuboid instead and it's not failing anymore.
usually that means you have NaN or Infinity somewhere
e.g. in the position or rotation, or an invalid shape
I'm using a PrismaticJoint for cylinder and cuboid. I want the free axis to be aligned with the cube, so I added a system that adjusts the axis on FixedUpdate, after which the assertion started failing. Replacing the cylinder with another cuboid and it works again
Appears that the global transform of the main cuboid turns NaN at some point.
Yup that'd be the cause
the joint probably becomes unstable and blows up for some reason
appears so. Is it "illegal" to alter the free_axis? 😆
I'm assuming it's a global orientation by default, and I'm trying to make it align with the main body in order to emulate a wheel suspension
Hah... of course, I'm translating the vector
sorry, my math is out of wack
I think free_axis determines which local axis the bodies are aligned to and can translate along. For example a free_axis of Vec3::X means that the axis goes through the local X axis of each body
OH, that makes sense. Ok, no need to adjust it then.
Thanks 🙂
The assertion is a bit scary tho. I wouldn't want my game to shut down even if something explodes. I guess the new failing systems in 0.16 could probably let me configure that if the update_aabb system will return errors then?
It's a debug_assert so it won't panic in release builds regardless
I should've observed that 😅 good point
But yeah at least the error message could be improved, it's not immediately clear from the current error what the problem is
and yeah we could maybe make this fallible in 0.16 if it doesn't impact perf
Neat! 😄 Thanks again
I believe ShapeCastData 's point1 and point2 should say they're in local space
Data related to a hit during a shapecast.
They're not
pretty sure it's world space but relative to the centers of the shapes
Or no I think it is fully global coordinates
not from my experience
I had to do shape_origin + point2 to get world space position
from the result of SpatialQuery::cast_shape
Made this test scene quickly, at least here it definitely is global coordinates
use avian2d::prelude::*;
use bevy::prelude::*;
fn main() {
App::new()
.add_plugins((DefaultPlugins, PhysicsPlugins::default()))
.add_systems(Startup, setup)
.add_systems(Update, render_rays)
.run();
}
fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<ColorMaterial>>,
) {
commands.spawn(Camera2d);
commands.spawn((
Collider::circle(20.0),
Transform::from_xyz(100.0, 0.0, 0.0),
Mesh2d(meshes.add(Circle::new(20.0))),
MeshMaterial2d(materials.add(Color::srgb(0.2, 0.7, 0.9))),
));
}
fn render_rays(mut gizmos: Gizmos, spatial: SpatialQuery) {
let origin = Vec2::new(-200.0, 0.0);
// Render a dot at the origin.
gizmos.circle_2d(origin, 2.0, Color::srgb(0.9, 0.2, 0.2));
if let Some(hit) = spatial.cast_shape(
&Collider::circle(10.0),
origin,
0.0,
Dir2::X,
&ShapeCastConfig::default(),
&SpatialQueryFilter::default(),
) {
// Render a line from the origin to the hit point.
gizmos.line_2d(origin, hit.point1, Color::srgb(0.2, 0.9, 0.2));
}
}
the green line is drawn from the origin of the cast to the hit point, which is global
oh wait what
hit.point2 is different okay
yeah there is something very weird happening there 🤔
can anyone provide any examples for how Sleeping is meant to be implemented?
What is the reason why SpatialQuery doesn't have a method for getting ContactManifold ?
You can use shape_intersections to get entities intersecting the given shape, and then manually use contact_query::contact_manifolds for each of the colliders
I guess we could maybe have a built-in spatial query for this too, though I haven't seen any other engine have that
I kind of answered here already #general message
depends on what you mean by "implemented"
like do you mean how sleeping is meant to be used and configured, or how Avian implements sleeping?
Thank you for the good documentation on this library. It's so nicely laid out ❤️
I'm trying to use colliders for an "ai vision" system. I have sensor colliders on the "vision sensors" and regular colliders on "vision targets".
This all works, but I'm trying to define collision layers for this to reduce noise. I have a VISION_LAYER.
Can I set up the layers such that the sensors detect collisions with targets, but not the other way around?
I tried setting the layers to:
let target_layers = CollisionLayers::new(VISION_LAYER, LayerMask::NONE /* filter */);
let sensor_layers = CollisionLayers::new(LayerMask::NONE /* member */, VISION_LAYER);
And this doesn't work... cause as I understand it, both colliders need to match filter/members.
So this works:
let target_layers = CollisionLayers::new(VISION_LAYER, VISION_LAYER);
let sensor_layers = CollisionLayers::new(VISION_LAYER, VISION_LAYER);
But then the target colliders pick up collisions with sensors, which is undesirable
Are "asymmetric colliders" not possible?
Collisions are always kind of undirected, it's not "A collides with B" but "A and B have a collision"
We should add support for per-entity collision events with observers or one-shot systems eventually, but internally collisions will still be stored as contact pairs like that
Ah fair. I could probably just do the filtering on my side
Also kinda unrelated but I'm most likely making collision events opt-in with a CollisionEventsEnabled marker component, very rarely do you actually want collision events for everything
Rapier and Box2D have a similar thing
I mostly use ContainedEntities. It's my favorite thing lol
With Changed<ContainedEntities>, it's great!
*CollidingEntities!
Mhmm, I was considering removing it since the new contact graph stuff will allow a similar thing (collisions.entities_colliding_with(entity)), but I'll probably keep it. It's still slightly faster than querying the graph, and the ergonomics of a component for this are nice
this is becoming rather big
a lot of things are sooo much nicer and more optimized though
Would y'all agree that where applicable, marker components are nicer than bitflags in the API? For example, this:
commands.spawn((
Collider::capsule(0.5, 1.0),
ContactPairFiltering,
ContactModification,
));
is nicer than this?
commands.spawn((
Collider::capsule(0.5, 1.0),
ActiveCollisionHooks::FILTER_PAIRS | ActiveCollisionHooks::MODIFY_CONTACTS
));
bevy_rapier has a lot of the latter, but I feel like these sorts of bitflags are often exactly what marker components are for in an ECS :P
also plays nicer with required components
and of course query filters
I imagine Rapier's reasoning there is that the internal collider structures use bitflags, and often for e.g. contact pairs you do also want to use bitflags, but using them for the API for body definitions seems very strange to me in an ECS
so I think I will go for marker components for enabling collision events and various hooks
I think it depends a bit on the specifics, but usually marker components are nicer yea, the only exception would be when the flags directly relate to eachother 🤔
Or alternatively if they are all literally part of the same thing, like collision layers, or for non-physics stuff, something like file permissions would be an example of that
yeah things like collision layers kinda need to use bitflags
they both become ugly once you actually have to use them though
I guess one minor benefit of bitflags for e.g. these collision hooks is that you can concisely do ActiveCollisionHooks::all(), but for marker components that's just a bundle :p