#Avian Physics
1 messages · Page 35 of 1
ball-ball, cuboid-cuboid, capsule-capsule, capsule-cuboid, cuboid-triangle, convex-ball (where "convex" is any convex shape), and a few other primitive shape combinations are pretty optimized and efficient, the rest mostly use GJK and EPA and are probably a bit more costly. For more complicated geometry, convex hulls are good, and convex decompositions are also good (though costly to build and can be slightly more expensive than a single convex hull since they decomposition produces a compound shape), and then there's trimeshes
Trimeshes can be fine too as long as the triangles aren't too small, causing things to compute contacts against many triangles at once
We can improve that on the Avian side a bit with manifold reduction, merging manifolds with similar normals and pruning points, but we don't do that yet
I mean if we're truly talking peak efficiency for just collisions, spheres are hard to beat ... But if they are dynamic rigid bodies I'd imagine cuboids end up being less costly because they stabilize far more quickly
Like realistically a sphere is just a point as far as the math goes, and a capsule is just a line ... These are really hard to beat 🤣
I was surprised a sphere would.. but I guess that's just a distance check from a point... that makes sense
yeah
yeah I mean this is the entire contact manifold computation for a ball-ball contact
vs. for most polytopes you need to run GJK (and EPA if there's penetration), get the supporting features, clip the polygons to get the contact manifold, run normal correction stuff for some shapes, etc.
Thank you for your thorough and detailed reply! It seems I will have to work around the issues I was having; just for a bit of context, I am simulating orbiting spacecraft and and was experiencing quite a bit of energy gain over time. I suppose I'll just fix it by putting the spacecraft "on rails" when in orbit 😁
sorry for the side-topic, is this just iyes_perf_ui for the visual portrayal? or something local/bespoke?
Built-in in Avian! See
https://docs.rs/avian3d/latest/avian3d/diagnostics/index.html
Diagnostics support for tracking physics timers and counters. Useful for profiling and debugging.
added in 0.3
thanks!
Hi @thorn solstice! So I've finally been working on the BVH broad phase more. There's still a lot to optimize and fix, but I have the basics working, and have some initial comparisons of different techniques.
So, for builds, I've tried the following:
- Full rebuild: Just build the whole BVH from scratch every time with
PlocSearchDistance::Minimum. Other configs seem to be way more expensive. - Remove and insert: Use
remove_primitiveandinsert_primitivefor each moved proxy/primitive. - Resize and reinsert: Use
resize_nodeto enlarge each moved proxy and refit their ancestors, andreinsert_nodeto find a better spot in the BVH. - Resize and optimize: Use
resize_node(or I also made a simpler custom version) for each moved proxy, and then later run theReinsertionOptimizeron the whole BVH. The reinsertion can ideally be done in parallel with the narrow phase, partially hiding the cost. Right now I'm using abatch_size_ratioof0.5, which seemed to be the sweet spot.
I also have Parry's Qbvh with full rebuilds for comparison.
The test scene is 10k sphere colliders that have a 25% chance of moving in a random direction at each tick, simulating a fairly dynamic but controlled scenario. The narrow phase and solver are disabled here to mainly stress the BVH building and traversal. I adapted this from Box2D, but am using 3D to make it a fairer comparison with Parry, since it has 2D-specific AABBs and optimizations that OBVHS doesn't.
The approximate results for the total step times are:
- Full rebuild: 4.5 ms
- Remove and insert: 6.25 ms
- Resize and reinsert: 4.9 ms
- Resize and optimize: 6.7 ms
See the individual timers for a more detailed breakdown. "Other" contains stuff unrelated to the BVHs.
(Note that "Update AABBs" here includes both recomputing ColliderAabbs, which isn't parallelized yet, and incremental updates to the BVH. The base cost without incremental updates can be seen with full rebuilds.)
So, full rebuilds seem to be the fastest by a pretty clear margin here, and it produces the highest quality tree (along with the ReinsertionOptimizer using a high ratio). So I might end up using that for dynamic and kinematic bodies. For the static/sleeping tree, I'll probably just use insert_primitive and remove_primitive with occasional reinsertion to optimize things (or maybe full rebuilds for batch spawns).
However, I still haven't tried Box2D-style partial rebuilds. My understanding of how it works is:
- Shape proxies are incrementally added to / removed from the BVH when the shapes are spawned/despawned or teleported. The
insertion_removalbranch for OBVHS makes this possible :D - At the end of the time step (in
b2Solve), serially enlarge broad phase proxies (withb2DynamicTree_EnlargeProxy). This updates the AABB of the enlarged proxy's node, and refits ancestors, marking them as moved nodes using a simpleu16bitflag stored on each node. Early out when an ancestor is unchanged and its ancestors have already been marked as moved. No reinsertion or optimization is done here. - During the next time step, the broad phase runs like normal, and finds intersecting pairs of AABBs.
- In parallel with the narrow phase, perform a partial BVH rebuild (in
b2DynamicTree_Rebuild) to keep the tree quality good. I believe the gist of how it works is:- Gather all proxies/primitives that have grown (= marked as moved), and all internal nodes that haven't grown. Both are considered to be leaves in the rebuild. Free internal nodes that have grown.
- Then, I think you just build the tree like normal, using the gathered nodes? Not well versed enough on BVHs to know the details here. Box2D defaults to a median split heuristic, which Erin has claimed builds 10x faster than 8 bin SAH.
I kind of tried this with my "Resize and optimize" approach using ReinsertionOptimizer instead of proper partial rebuilds, but clearly that wasn't ideal. I believe these kinds of partial rebuilds could be the fastest option overall based on Box2D's results though. It makes the serial portion cheap (just refitting AABBs) and hides the cost of the rebuild by making it run in parallel with the narrow phase. But it does potentially require some changes to OBVHS, like storing flags on the nodes.
/end_essay
So I guess the TLDR is: It seems like the stuff on the insertion_removal branch works pretty well, and I intend to use the incremental insertion/removal for the static/sleeping tree and for spawned/despawned shapes. I'd still like to try Box2D-style partial rebuilds for the active tree, but full rebuilds also work well, and are definitely faster than Parry's full rebuilds :)
this is art.
My wishlist for hypothetical future physics-oriented stuff in OBVHS would probably be:
- Bitflags on nodes for stuff like marking them as moved (for partial rebuilds)
- A bitmask on nodes (including internal ones) to allow early-outing for incompatible layers, useful for both collisions and spatial queries like ray casts
- Built-in Box2D-style partial rebuilds
- 2D AABBs
f64 AABBs (for large worlds)(probably not too important)
f64 AABBs (for large worlds)
I wonder if there isn't a better approach here
Going for f64s would be a big slowdown 🤔
you could have multiple BVHs around different origins
and make queries relative to that
which is vaguely related to big_space support too
doesn't big_space have a better precision for both extremely small worlds and extremely large worlds? if you can support it, then i don't see the point in f64
yeah so I checked Jolt and it also uses single-precision floats for the broad phase and BVH
it allows using f64 for ray casts but drops down to f32 for the BVH traversal and seems to only use f64 for the actual intersection test for shapes
so it accepts that the broad phase (and BVH) is inaccurate and less effective at large distances from the origin, but supports f64 for the narrow phase queries where precision matters for stability and accurate results
which seems reasonable
okay or more specifically, for e.g. ray casts, Jolt supports an f64 ray, but for the actual query it transforms the ray to local space and then drops down to f32 for the actual ray cast on the shape
and for shape casts it uses a base offset
Awesome! Thanks for trying it out! Note: on Resize and optimize you can use ReinsertionOptimizer::run_with_candidates() to have it only look at the nodes you need in case you didn't notice it.
Also with full rebuilds I don't think I would have thought you would use the ReinsertionOptimizer since it can take a while (at least given enough primitives/nodes). Were you finding that traversal speed was a lot slower without it?
If median split here is LBVH, that is basically the fastest way you can build a BVH afaik. but the resulting bvh quality is extremely low. PLOC is supposed to be almost as fast to build while producing a dramatically higher quality BVH. I also still need to add HPLOC which should theoretically build even faster while maintaining similar BVH quality.
Also HPLOC should be even more readily parallelizable since it usually runs in a single compute shade dispatch
(Right now the actual BVH PLOC in obvhs isn't parallel since TinyGlade just needed single threaded to be as fast as possible)
I don't, I only use it for the "Resize and optimize" thing. For full rebuilds I just do this
self.bvh = PlocSearchDistance::Minimum.build(aabbs, indices, SortPrecision::U64, 0);
same as your physics example essentially
I'll try run_with_candidates too
Ah ok, so no ReinsertionOptimizer in this case.
I'm not 100% sure but I don't think it's LBVH. Erin mentioned LBVH earlier too and also mentioned that it has very fast builds but much slower traversal. For the heuristic used, Erin specifically said this (on the Box2D Discord)
(ignore the overly bright screenshot, discord does that for some reason when I take screenshots)
the median split stuff is here fwiw
https://github.com/erincatto/box2d/blob/4b5e72bd44bd139fab67ad890b70e1690ff405a8/src/dynamic_tree.c#L1426
it uses the Hoare partition scheme and something, I'm not familiar with details
Box2D does have SAH too as an option, but defaults to median splits since they were faster for its use case
I want to be really careful regarding adding complexity to the base BVH/BVHnode. Ideally you could manage this meta data using the primitive_indices & primitives_to_nodes mappings . But if that won't work because you need the better cache locality of having it in the node, or for some other reason, we'd need to figure out something with generics or something so it doesn't regress perf for users that don't need it. I guess since you would need meta data on inner nodes, that may not work anyway. Will have to think about how to do this cleanly. I would love to keep obvhs as focused on just the bvh building side of things as is reasonable.
may be your monitor ICC profile sneaking its way in
Yeah, totally makes sense. Some generic approach could maybe be ideal, but of course we need to be careful not to regress perf there. I could also just copy what I need from the Bvh2 locally in Avian, and adapt it to be more optimized for physics (assuming it'd actually improve perf for my use case), though it would of course be great if I could depend on OBVHS directly
I wonder if it is possible to have the generic part no-op? Or would it have to be a generic behind a feature?
If there's a Meta generic on every node, I guess it would need to have some traits for dealing with when the bvh needs to make new nodes, etc..
I'd also like to at least try 2D AABBs so it'd require the AABB type to be generic or alternatively just choose if it's 2D or 3D based on feature flags
Feature flag doesn't sound ideal here tbh 🤔
ofc if it doesn't end up actually giving any perf boost or a meaningful reduction in memory usage, then 3D-only is fine I guess
Yeah, would have to see what that looks like. Would also have to see if that kind of generic has any impact on perf. There were some surprising areas where I had to avoid generics because it had a large perf impact due to how it affected inlining
Like if you want an optimal 2D BVH, you're probably just gonna structure things different and get to do some extra optimizations at which point the feature gates will be everywhere
It's almost certain to give a meaningful reduction I think ... You can fit things in a single SIMD register instead of dealing with 2 partially full ones
Yeah, with 2d there's probably a lot of SIMD shenanigans to be had.
As much as it feels bad, it might just be better to have it be a separate thing
Still could have a place in obvhs, but in a separate mod, as totally separate type, etc..
Yea, or you'd need to be able to figure out the generics for that very well, but you'd still end up with substantially different impls for your types and possibly some more niche than you'd expect functions
Maybe it's that they are separate, but they both implement some traits that allow some other code that could be shared to operate on both, or something
I feel like it probably takes playing with making everything work with a Aabb2d and then noting what exactly changed and what things could then work differently.
I wish rust had more specialization tools like comptime stuff, but I understand that also can become its own can of worms
That would be a decent approach too I guess, allows you to interact with BVHs in the same simple way as what I had in ploc-bvh where volumes and intersection tests are generics, but provide more room for optimization, rather than room for extensibility 🤔
The only interesting extensibility would be maybe OBBs anyway, and that might still be more efficient as a second pass ... The bounding sphere BVH I support in ploc-bvh is a mistake 
clearly we need a BVH with k-DOPs
Time to accelerate our hierarchy using a BVH!
The worst part is I looked up k-dops and see "accelerating raytracing with kdops" and "2.5x performance" ... what
Real-Time Collision Detection has a whole section on them for some reason
they're "discrete-orientation polytopes" that are slab-based volumes with normals that are a fixed set of axes among all k-DOPs in the scene
ahh yeah that hpg talk a while back: https://www.youtube.com/watch?v=DyjdHtTAYK4
Building is slower though, which I think with physics is more the bottleneck, but idk
they're very cheap to store and the intersection test is similar to AABB-AABB intersection tests
I guess collision detection can use them directly though so..
but yeah idk what/who actually uses them
I just wish we could know what nvidia is doing since their stuff is now so ridiculously faster than AMD's (which is HPLOC with something CWBVH-like I think)
Honestly I've been considering adding a step between raymarching and the AABB to further reject volumes
iirc in your case the primitive traversal portion (raymarching) costs a lot more than the expected triangle, so I could definitely see something like that being worthwhile
Yea, same could be true for physics when the collider is an expensive type. But plenty of colliders can be trivially cheap to check
Could have something like a baked quantized SDF matrix. Could be maybe ~8 bits per cell, and then basically fit 2x2x2 into a u64
AI ✨ (probably not actually, idk)
there are actually some papers about using deep learning for building trees lol
a quick google brings up this neural builder thing to infer the structure of a k-d tree
https://link.springer.com/article/10.1007/s00371-023-02975-y
There is a type of SDFs involving AI 
of course there is
Neural Implicit Surfaces iirc
I think I saw a physics solver using AI for something
then there's also that one project that can supposedly generate and simulate almost anything very fast?
I bet it would explode even harder than bevy_xpbd on that pyramid of boxes getting hit by a big circle :D
A new physics paper I'm actually kinda interested in is the new paper on Reliable Iterative Dynamics (RID), published just this month
https://dl.acm.org/doi/pdf/10.1145/3734518
It's closely related to XPBD and VBD, but.. based on their results it seems to just be better and faster
deals better with high stiffness and has good results even with very few iterations or substeps
but that paper only showcases it for soft bodies and cloth, idk how applicable it is for rigid bodies
and it still has limited propagation of information like most solvers, though it could maybe be improved with a multigrid approach
Kind of a dumb question, I see ContactData has a point1 and point2 that are the "contact point on the nth entity in local coordinates." How do I get the global coordinates from the local?
add Position
Hey guys I'm trying to recreate pong as practice for my bevy game. I have 2 paddles and a ball as dynamic objects. I'm trying to figure out how to make it so LinearVelocity never depreciates for the ball. I've tried setting Restitution, Friction, and LinearDamping to 0 (and tried every combination of CoefficientCombine), and it still seems like every time the paddle hits a paddle it loses some momentum. How do I make sure the velocity always stays the same?
restitution=1, friction=0, and gravityscale=0 on the colliding objects are all I've needed in the past. You may be running into the velocity transferring into rotation or similar as well.
I tend to set them globally in these kinds of use cases:
.insert_resource(DefaultFriction(Friction::new(0.)))
.insert_resource(DefaultRestitution(
Restitution::new(1.),
))
hmm i tried setting that. didn't work. i tried setting just the paddles to have Collider::Static and that works. the ball doesn't lose momentum, but they don't work with LinearVelocity for moving
try locking the rotation of all involved bodies as chris mentioned
with the AngularDampening(0.0) component? I have that set on all 3 bodies. Also currently the ball is moving directly horizontal and the paddles aren't moving.
A component that specifies which translational and rotational axes of a rigid body are locked.
yup just found it sorry. so the paddles have LockedAxes::ALL_LOCKED.unlock_translation_y(), and the ball has LockedAxes::ROTATION_LOCKED. not working...
"not working" doesn't describe a problem we can help with
no matter what i do it seems like the ball loses a bit of momentum every time it hits the ball. the only thing that fixes this is setting the paddles to RigidBody::Dynamic but that makes it so you can't use LinearVelocity to move the paddles
let me capture a video
dynamic rigid bodies are affected by forces, including the force of the ball on the paddle
also let me update the gh repo if you wouldn't mind taking a look
if you had free floating dynamic rigid bodies colliding, you'd have moving objects after the collision transferred momentum
you probably want kinematic for the paddles
that was it
appreciate it
note that you may still see the balls slowly losing energy over a long period of time because of speculative contacts, contact softness, the semi-implicit Euler integrator, etc.
if you want the speed to stay fully constant, you may want a system that clamps the magnitude of the velocity every frame or after contacts
How do I manually set moment of inertia for a collider?
You can set the AngularInertia
The local angular inertia of an entity, representing resistance to angular acceleration. A higher angular inertia requires more torque for the same acceleration.
(note that in 2D it has a simpler representation and API, so look at the docs for that if you're using 2D)
I got smallish moment of inertial defined in URDF:
<inertial>
<origin rpy="0 0 0" xyz="0 0 0" />
<mass value="0.027" />
<inertia ixx="1.4e-5" ixy="0.0" ixz="0.0" iyy="1.4e-5" iyz="0.0" izz="2.17e-5" />
</inertial>
Once I set it in avian my rigidbody gets inf transform
So here's my experiment. I do the same stuff in rapier, avian and comparing to a reference mujoco impl.
The quad-copter is modelled as material point with center of mass (0,0,0), defined angular momentum. There's weightless collider.
I start with an altitude controller that should keep a drone on a defined velocity.
Then I add gains which make dynamics more sophisticated but it's pressty simple in the beggining.
where's what I get in avian applying a force to a center of mass of a cyllynder
in rapier it only moves on z axis with identical dynamic model
There's no external torques acting on body, but it rotates
let rotor_1_position = Vec3::new(0.028, 0.0, -0.028);
let rotor_2_position = Vec3::new(-0.028, 0.0, -0.028);
let rotor_3_position = Vec3::new(-0.028, 0.0, 0.028);
let rotor_4_position = Vec3::new(0.028, 0.0, 0.028);
let f = Vec3::new(0.0, 1.0, 0.0);
let f1 = f * event.thrusts[0];
let f2 = f * event.thrusts[1];
let f3 = f * event.thrusts[2];
let f4 = f * event.thrusts[3];
let full_force = rotation * (f1 + f2 + f3 + f4);
*external_force = ExternalForce::new(full_force).with_persistence(false);
What's the collider? And are there any collisions happening (I assume not?)
oh a cylinder
Yeah it definitely shouldn't start rotating if there are no collisions or anything and you're applying the force at the center of mass 🤔
Started setting up some command line tools for nicer benchmarks (these are not reliable yet since it only runs each bench once atm)
The goal is that I can just run a binary, and it runs all (or specified) benchmarks with different numbers of threads, and with different SIMD targets (AVX, SIMD128, NEON, SSE2, scalar), and optionally produces nice CSV files that I can then easily create graphs from
similar to Box2D's benchmarks
https://box2d.org/files/benchmark_results.html
Rapier also has something similar but I think it's outdated and not working nowadays
it'll be nice for actually seeing how performance and scaling has improved, and also for comparing with e.g. Rapier
First such graph
the perf boost on main is just from solver bodies :P
Wait whoops the units are wrong here 😂 the numbers are obviously seconds but axis says ms
okay this is more like it
The curve is quite flat right now, since the narrow phase is really the only major thing that is multi-threaded right now. But both the BVH broad phase and especially the graph coloring stuff should improve multi-threaded scaling quite drastically
Yea the thread scaling looks a bit sad 🥲
For reference, Box2D looks like this
Now that looks more like actual scaling
(ignore that we are technically a lot faster with one or two threads in pure numbers, it's different hardware)
I mean the fact that box2d goes almost twice as fast with 2 cores vs 1 suggests it doesn't have efficient single thread paths, that could play a factor too
Single threaded perf is somewhat niche of a usecase though, maybe online games where you want to maximize the number of instances you can run
iirc single-threaded Box2D v3 is still like twice as fast as v2 (which didn't have any multi-threading)
but yeah
so... turns out this was because my pyramid was not actually 5050 boxes, it was way smaller 💀 I really should triple-check these things
I made a new plot with the correct results, this time comparing my WIP graph coloring branch to main
especially with the graph coloring, scaling isn't quite as miserable, but still not Box2D-level
Lol
mmm I really want to see bevy_rapier on this plot too
Ah thats better 😄
might not be too hard actually with how I've set this up, with the CLI being physics backend agnostic
eh, tomorrow :P
So I was looking at the plot in disbelief of how fast Rapier is... Note to self: remember to disable sleeping for benchmarks so you're actually simulating something 😂
I made a proper plot now though, using bevy_rapier2d 0.30.0 with parallel and simd-stable enabled, trying to match the configuration I have for Avian as closely as possible (60 Hz, 4 substeps, 1 relaxation pass). I'm pretty happy honestly
So Rapier is still much faster when single-threaded, however its profile is extremely flat here because this is a single large pyramid and Rapier only parallelizes across islands
With graph coloring, we beat Rapier quite clearly in this scene, and will do even better (even when single-threaded!) once we have wide SIMD
I'll try to do the many_pyramids benchmark next to simulate a scenario where there are many islands, Rapier should parallelize better there
Avian more performant than Rapier confirmed!
-# in one benchmark
Yeah so with a more realistic benchmark of a bunch of separate smaller pyramids, Rapier scales slightly more and is faster overall
Add something very slow on it so the y axis expands and it looks like rapier and avian are roughly the same /j
Interestingly the graphs seem to have nearly the same shape
You sure rapier scales more?
Just eyeballing it, looks about the same
Can you overlay them so they start at the same point for comparison?
However, looking at where our costs are, the solver is only about 2-3 ms (with full threading)! So a lot of the cost is actually coming from the broad phase, Qbvh updates, and "Other" stuff, which should be reduced by the OBVHS stuff and various miscallaneous optimizations
Sorry I meant Rapier scales better in this benchmark than the one with the single large pyramid, not better than Avian 😅
Aaah makes sense!
Time to close the "Time to optimize" issue soon? 👀
Once we're reliably faster than Rapier in both 2D and 3D 😤
Is reliably as fast as rapier not good enough?
Not for me :p
imo Rapier's lack of graph coloring kinda limits it, with graph coloring and proper wide SIMD we should be quite a bit faster than it
nah otherwise rapier won't be motivated to get faster, which will push Jondolf to further optimise and so on until we have instant physics
You’re right, that’s a good plan 
looking at Rapier's source code there's a lot of cool optimizations I haven't seen elsewhere, but a lot of things also seem way too overcomplicated to me
I'm also curious how our joints will compare to Rapier 🤔 Rapier uses generic 6DOF-ish joints for all joint types, I'm aiming more for Bepu-style joints with custom vectorized implementations of each type of joint
just need to somehow batch each joint type separately for graph coloring
I feel like Avian 0.4 will genuinely have over 10x the performance of 0.3 in heavier scenes 😂 the solver body stuff alone is a 2-3x improvement in a lot of cases
it'll make for a good blog post
Also just to highlight Box2D's performance: Box2D's version of this benchmark has four times the pyramids, and on my machine it's only 3.19 ms per step.
So yeah... that's the kind of perf I'm ideally aiming for, it kinda leaves Rapier in the dust :P
Want foxtrot benchmarks? I thinks that's a fairly realistic 3D scene, though it's not that many objects
So 4x more pyramids at about the same speed? woah 👀
Rapier somehow takes 26.27 ms with 8 threads in this scene 😂
idk if there's something going horribly wrong there though, I'd need to check the internal timers for a detailed breakdown
It's probably too minimal, IIRC it only had like a few dozen to a hundred contacts at once while these stress tests have thousands
that Box2D scene has 58,000 contact pairs
and 22,001 bodies
lol, even when single-threaded, Box2D is 16 ms while Rapier was 48.6 ms
graph coloring and wide SIMD are probably doing some heavy lifting there, though it still doesn't explain that drastic of a difference
i want to figure out what the max velocity of a body would be for a given LinearDamping and max ExternalImpulse - assuming the maximum impulse is applied in the same direction every timestep and there are no other forces.
is the velocity gain from an impulse just the vector length divided by the mass?
believe so
tough part is figuring out LinearDamping, since it's computed as v[t+dt]=v[t]*1/(1+dt*damping) and therefore is dependent on the fixed timestep (unlike eg v[t+dt]=v[t]*(-damping*dt).exp2())
might be different due to substepping, but if my understanding is correct you'd only be applying it on the first substep of every update anyway
In practice the difference between the two is extremely small, you can see a comparison here
https://github.com/Jondolf/avian/pull/633
The former (which Avian uses) is the Padé approximation of exponential decay, while the latter is the actual formula for exponential decay
Yeah it's equivalent to just doing lin_vel.0 += lin_impulse / mass in a system yourself
with the upcoming force rework it will also be literally doing that, applying the impulse to the velocity immediately rather than storing it and applying it some time later
ok, cool. so i should be able to just find when the velocity loss from damping equals the gain from the impulse yeah?
thanks for the help
neat
Hello, I am rendering these 3 objects with a second camera with a different field-of-view and higher order with respect to the existing camera. I am using the same RenderLayers layer for both the camera and the objects. Why are the colliders spawned at a different location than the mesh? What's even weirder is that they are pickable objects and both the mesh and the collider result in pickable behaviour. Also, they are linked, so hovering over the collider will have an impact on the mesh. Thanks!
I've found using bevy-inspector-egui really helpful for understanding why stuff like this happens.. it's usually the hierarchy of things not matching what I expect
I also use the inspector but I couldn't find any clues. In fact, the colliders position is the position the objects would have if I rendered them with the main camera. I thought that adding RenderLayers in the same bundle together with the mesh and collider would apply the layer to the collider too?
Is there a preferred way to disable gravity entirely? beyond
fn main() {
App::new()
.add_plugins((DefaultPlugins, PhysicsPlugins::default()))
.insert_resource(Gravity(Vec3::ZERO))
.run();
}
can't immediately tell which plugin it's used, or if it's used in several
What is the proper way to attach a sensor to a dynamic body? I tried to just add a sensor as a child on a dynamic body but that didn't work
I was just about to ask this question lol^
I ended up using spatial queries instead to do a shape intersection check
yeah thats what i thought as well
Doesn't work in what way? Remember to add CollisionEventsEnabled to the sensor entity to get collision events if you're not getting them
well the sensor is working but i expected adding the sensor as a child would propagate the transform from the parent
im realizing now i could also just do this with a joint
It should, as long as both have a Transform
Also make sure you don't have RigidBody on both the parent and the child
only on the parent
you want one rigid body with shapes attached to it, not several different bodies
No worries 😄
that's a fairly common mistake, I wonder if we should emit some warning for it 🤔
parenting bodies to other bodies is generally kinda questionable, especially with how it's currently handled, but I guess in some cases it could have uses
well base bevy doesn't even have warnings for incompatible components, like putting a text2d and mesh2d on the same entity. There should probably be an example or documentation for putting multiple colliders on a body tho
There are docs
https://docs.rs/avian3d/latest/avian3d/collision/collider/struct.Collider.html#multiple-colliders
A collider used for detecting collisions and generating contacts.
but yeah we need more/better examples
I made the exact same mistake lol. But I was coding on a plane so I didn't have example access.
I suspect the reason I made this mistake is because it works in Godot! And lots of people using Bevy come from Godot. With the built in physics API you can attach an Area to a Kinematic or RigidBody and they're just sort of totally distinct concepts. I suppose that's actually the case here too it's just not as obvious because you can still just attach a RigidBody component to an entity with a Sensor and it will run, it just won't do what you expect.
hello friends, anyone have tips for figuring out why my chain is just kinda going through walls if it wants to? it's made of chain links like this
RigidBody::Dynamic,
MassPropertiesBundle::from_shape(&Circle::new(particle_radius), 1.0),
Collider::circle(particle_radius),
CollisionLayers::new([CollisionLayer::Chain], [CollisionLayer::Wall]),
and connected by
DistanceJoint::new(last_spawned, current_particle)
.with_rest_length(2.0)
.with_limits(1.5, 2.5)
.with_compliance(0.000001),
and the walls are RigidBody::Static, but the distance joints seem to just. pull the links right through the wall with no resistance at all
they do collide, when it's at rest the chain rests on the ground correctly
it's just that i suppose under tension they totally ignore the collision
Here I drop a 27g cylinder (6cm radius 2.5 cm height) with (0,0,0) center of mass from 10 cm height. Friction is set to 1.0
it drifts on both z (first axis) x axis (p2) and y (! pic3).
Am I missing something?
Is there an easy way to pause physics systems?
time.pause()?
Thanks
how are you making the collider for the wall?
should the PhysicsPickingPlugin bubble events up the hierarchy if a child collider is "picked"?
they're tiles spawned in by bevy_ecs_ldtk, i attach a square collider that's exactly the size of the tile (confirmed via physics debug plugin)
hmm.. I've had situations where rigid bodies pass through colliders if they're made with trimesh since that's hollow.. are you using Continuous Collision Detection?
https://docs.rs/avian3d/latest/avian3d/dynamics/ccd/index.html
Contains components and functionality for Continuous Collision Detection.
oh oops, worth noting I'm working in 2d! I haven't tried ccd yet though, i figured it probably isn't gonna fix whatever problem is causing the joint force to completely trump penetration resolution, I'll give it a try though
I don't have a graph like yours to visualize drift, but just looking at it visually it seems fairly minor. In a game this would definitely be marked as sleeping (I had to add SleepingDisabled to even demonstrate the drift at all)
But yeah, small objects tend to be more prone to stability problems currently
Also worth noting that physics engines tend to be kind of tuned to a range where they work best. For example, both Box2D and Jolt state that dynamic bodies should be in the order of [0.1, 10] meters in dimensions.
Box2D has been tuned to work well with moving shapes between 0.1 and 10 meters. (source)
In order for the simulation to be accurate, dynamic objects should be in the order [0.1, 10] meters long, have speeds in the order of [0, 500] m/s and have gravity in the order of [0, 10] m/s^2. (source)
But yeah, stability could still be improved here
Also you can use PhysicsLengthUnit to scale some of the internal tolerances and improve behavior at different scales (e.g. if you want to use pixels in 2D), but if there are e.g. numerical issues, it doesn't help there
A units-per-meter scaling factor that adjusts the engine’s internal properties to the scale of the world.
I haven't seen so much problems at large scales though, especially in 2D, it has been more-so small shapes in 3D that I've seen having stability issues like jittering or easily phasing through stuff
so it's very possible there's some actual bug there too (related to inertia or friction would be my guess)
Intuitively my guess would be that it's because joints still use XPBD (which modifies positions directly) while contacts use an impulse-based solver (which modifies velocities). The joints can kind of just pull the bodies without properly considering the impulses applied by contacts. This is especially bad since the joints are applied after contacts, which kinda gives them "higher priority"
We are (or really I am) in the middle of reworking joints to also use an impulse-based approach, which should hopefully alleviate this. But some things that might help a little bit:
- Higher compliance for the chain (though it'll make it more stretchy...)
- Tweak
contact_frequency_factorandcontact_damping_ratioin theSolverConfig - Make the chain particle colliders bigger
- Make sure you have an appropriate
PhysicsLengthUnit(can be important if you're using pixels as units)
that makes perfect sense, i did find that adding more links alleviated the problem, but it has an undesirable effect on the chain physics - i'll try the SolverConfig stuff thanks!
Hmm 🤔 Does the MeshPickingPlugin do this?
It could definitely make sense to bubble to the rigid body through ColliderOf
Haven't tried with mesh picking, but I know the UI its standard behaviour.
I do think it would make sense fo the events to bubble up to the top rigidbody from child colliders.
Hmm if you observe a RigidBody entity that itself has a collider, and you trigger the picking event on some child collider, does it let you know which collider it was triggered for?
I think the target would be the child collider in this case
But how do you know that inside the observer 🤔
I think observers are missing the currentTarget equivalent that the web has
target should be the entity the event was triggered for, while current_target should be the entity the event handler (i.e. observer) is attached to
Wait no is observer the current_target based on the docs on target? But aren't observers satellite entities, why is observer the entity that the observer is attached to 
How is it handled in the UI?
Hello again, sorry if I post this again but I'm kind-of stuck. Does anybody have a clue on what the problem could be? Why would the colliders spawn according to the primary camera if the RenderLayer of the objects is the same as the secondary camera (that visualizes the meshes correctly)?
Some ideas:
- you have ribid bodies parented to other rigid bodies, which behaves very weird
- everything is working correctly, but the debug gizmos are being drawn on multiple cameras, making it look weird
Thanks a lot for your reply. On your ideas:
- the three objects have neither parents nor children
- even if the gizmos were the problem, why does hovering over them activate picking behavior on the actual objects? Are they bound to their colliders? If yes, the picking behavior should only work on the colliders/gizmos, but it works both there and over the meshes
(I just disabled PhysicsDebugPlugin and the colliders are still spawned there with picking behavior)
Mind sharing the spawn code?
it's really nothing special:
let _id = commands.spawn((
Name::new(format!("Object {}", i)),
Object,
RigidBody::Kinematic,
collider_mesh.clone(), // Collider::convex_hull_from_mesh(meshes.get(&scene_assets.object).unwrap()).unwrap();
Mesh3d(object.mesh.clone()),
MeshMaterial3d(materials.add(object.material.clone())),
Transform::from_translation(position),
RenderLayers::layer(OBJECT_RENDER_LAYER),
)).id();
and the secondary camera:
commands.spawn((
Name::new("ObjectCamera"),
ObjectCamera,
Camera3d::default(),
Camera {
hdr: true,
order: 1, // Bump the order
..default()
},
transform_new,
Projection::from(
PerspectiveProjection {
fov: FOV,
..PerspectiveProjection::default()
}
),
RenderLayers::layer(OBJECT_RENDER_LAYER),
));
i'm spawning an entity with RigidBody::KinematicRigidBody::Dynamic, LinearVelocity::ZERO, and ExternalForce::new(vec2(0.0, 300.0)).with_persistent(true), but it's not moving. any ideas?
looking in the inspector, if i set the external force to e.g. 70, i see the linear velocity increase for half a second then snap back to 0
the missile does move during that half second then stops
Kinematic don't react to forces, do they?
Shouldn't you set the velocity on them directly?
ah okay then it should be dynamic i suppose. this is a missile in game for context 😄
then there is a bug though
because it is reacting to the force
it just resets the linear velocity to 0 after increasing up to about 1.5 or w/e (and very slightly moving the missile)
if i set the external force high enough, it doesn't reset velocity to 0 and just takes off
oh wait mb, the missile is already a RigidBody::Dynamic
Try
let _id = commands.spawn((
Name::new(format!("Object {}", i)),
Object,
RigidBody::Kinematic,
- collider_mesh.clone(), // Collider::convex_hull_from_mesh(meshes.get(&scene_assets.object).unwrap()).unwrap();
+ ColliderConstructor::ConvexHullFromMesh,
Mesh3d(object.mesh.clone()),
MeshMaterial3d(materials.add(object.material.clone())),
Transform::from_translation(position),
RenderLayers::layer(OBJECT_RENDER_LAYER),
)).id();
Don't think this should behave any differently, but worth a try
hmm this is 2d physics, should i still try that?
Why not?
i saw mesh in the diff :p
can confirm that ExternalForce has no effect on the rigid body when it's kinematic 👍
That message was to pod, who uses 3D I believe 🙂
ohh ok
good to have that confirmed, thanks!
Pretty sure that the FromMesh stuff does not work in 2D for the record 😄
i'm having a strange issue where my tnua-controlled character vibrates spins out when there are other forces acting on it (from Avian), specifically it rotates left and right rapidly, despite having LockedAxes::ROTATION_LOCKED on it
anyone have any guesses? i'd assumed rotation locked would...you know...lock rotation
wait, just being clear, this is is actually RigidBody::Dynamic? or is the dynamic rigid body missile something else
no change unfortunately
it's dynamic yes
ok I'm gonna try to reproduce
lmk if i can provide more details for reproduction
any collider?
Collider::capsule(3.0, 7.0),
specifically:
(
Name::new("Missile"),
IsMissile,
Sprite::from_image(missile_assets.image.clone()),
RigidBody::Dynamic,
LinearVelocity::ZERO,
ExternalForce::new(vec2(0.0, 300.0)).with_persistent(true),
Collider::capsule(3.0, 7.0),
CollisionEventsEnabled,
Patch(|entity| {
entity.observe(hit_ship);
}),
)
you know what's strange
so for a second I was like, yeah, nothing's happening, then I added your capsule and it worked
oh so the opposite of what i was seeing? lol
use avian2d::{PhysicsPlugins, math::Vector, prelude::*};
use bevy::prelude::*;
fn main() {
let mut app = App::new();
app.add_plugins((DefaultPlugins, PhysicsPlugins::default()))
.insert_resource(Gravity::ZERO)
.add_systems(Startup, setup)
.add_systems(Update, log_transforms);
app.run();
}
#[derive(Component)]
struct Ant;
fn setup(mut commands: Commands) {
commands
.spawn((
Name::new("Ant"),
Ant,
RigidBody::Dynamic,
Collider::capsule(3.0, 7.0),
LinearVelocity::ZERO,
ExternalForce::new(Vector::new(0.0, 300.)).with_persistence(true),
CollisionEventsEnabled,
))
.observe(on_collide);
}
fn log_transforms(ant: Query<(&Transform, &LinearVelocity), With<Ant>>) {
let Ok((transform, lvel)) = ant.single() else {
info!("ant not here");
return;
};
info!(
"Ant at ({:?}), Velocity: ({:?})",
transform.translation, lvel
);
}
fn on_collide(_: Trigger<OnCollisionStart>) {}
yes, this is 3d though so let me see about 2d
i have zero gravity as well
ahhh. wait
If we look at the example of a custom event:
https://bevyengine.org/examples/ecs-entity-component-system/observer-propagation/
it seems like the collider which was hit would not be the target if you intercept it at the rigid body level. But if you observe it at the child collider you would get the collider hit.
Now is that logic missing from the general picking backend?
I think that information (which entity was originally picked) would be useful even when propagating events (physics and UI or anything else).
yeah after verifying, it is working with the above code in 3d.
edit: on switching to 2d, it behaves as expected
as another data point, switching to non-persistent force applied every frame fixes the issue
only thing I'm missing here is Patch and Sprite. Everything else is analogous in a vaccuum. I'm not sure why this isn't working for you, but is working for me
yeah idk.. the patch adds a Trigger<OnCollisionStart> observer
So the entity that was originally picked is stored in the picking event type as Pointer::target, which is (confusingly) different from Trigger::target, which seems to be more like the web's currentTarget. But yeah I believe we are missing the entity that the event was originally triggered for in the general case, it's only stored for the picking events
There's a lot of confusing and straight up wrong information in the docs though, I made a thread where I was asking about it https://discord.com/channels/691052431525675048/1379202565820125275
I've updated the test. can you try the above code on your computer and see if you get different results? has everything but a sprite
i'll make a note to come back to this in a week, the jam game must grow 😅
haha ok
does anyone have some point-of-contact code I could yoink for Trigger<CollisionStarted>?
doesn't need to be a super precise value, just in the ballpark
since it's dealing with spheres...i was about to just take the colliding entity's center, draw a line to the sphere's center and project onto the surface
might be the move
@vestal minnow I'm playing around with avian2d, but I'm using an ortho camera instead of a 2d camera. Now, the thing about an ortho camera is that you can orient it in any direction, but I presume that Avian physics is opinionated about which 2 out of 3 axes you use. You don't get a choice, right?
I have to decide which axis represents depth - I've seen examples using either Y or Z.
for a 2d game only Z makes sense, to reduce conversions
Did you ever get this working? I'm still struggling with a sensor collider that's a child of a dynamic RigidBody not registering any CollidingEntities.
did I miss a "heightfield-from-mesh" function somewhere?
IIRC parry does not directly support that :/
Yeah 2D physics is in the XY plane like if you were just using Camera2d. We could technically allow any plane, since physics positions are separate from the Transform, so the physics positions and visual positions don't need to match. This is possible with a custom version of theSyncPlugin that just maps coordinates differently
But I would generally recommend just working in the XY plane for 2D
If you were working in the XZ plane, components like LinearVelocity would still use Vec2, so you'd need to mentally map vel.y to mean vel.z everywhere
.observe(|trigger: Trigger<OnCollisionStart>, collisions: Collisions| {
let Some(contact_pair) = collisions.get(trigger.target(), trigger.collider) else {
return;
};
// OPTION 1: Iterate over all contact manifolds and their points.
// Iterate over the contact manifolds (kinda like contact surfaces).
// For convex-convex contacts there's only one.
for manifold in contact_pair.manifolds.iter() {
// Iterate over contact points in the manifold.
// For a circle or sphere there's only one.
for manifold_point in manifold.points.iter() {
// Use `global_point1()` and provide the translation and rotation
// to get the point in global space.
println!(
"Local point on `trigger.target` is {}",
manifold_point.local_point1,
);
}
}
// OPTION 2: Just get the deepest contact point.
if let Some(deepest_contact) = contact_pair.find_deepest_contact() {
// Use `global_point1()` and provide the translation and rotation
// to get the point in global space.
println!(
"Local point on `trigger.target` is {}",
manifold_point.local_point1,
);
}
});
dude that was so crazy, I literally just checked this channel
thanks!! hahaha
like just opened it immediately before you sent that
lmao
oh it works just instantly. that's beautiful
Or whoops, here you actually need to check which contact point is for trigger.target() and which one is for the other one (if you care) since the order in the contact pair is kinda arbitrary
you'd just check which one is contact_pair.entity1 and swap the points if necessary
oh, option 1 seems like it works. might be because it's a ball and stick collision or something
im only getting a few contact points in the same general spot
which is good
Right now the order is determined so that if trigger.target() is to the left of the other entity when the contact pair is created, it will be entity1, otherwise it'll be entity2
(this will change once we switch the broad phase algorithm to use BVHs though)
nw, this is essentially the same logic as major minor axes for ellipses which I have to do all the time at work unsually lmao
Have you added CollidingEntities to the sensor? You need to add it manually atm
a basic setup that should work is like
commands.spawn((
RigidBody::Dynamic,
Transform::default(),
children! [
(
Sensor,
Collider::sphere(0.5),
CollidingEntities::default(),
Transform::default(),
),
]
));
fn log_sensor_collisions(query: Query<(Entity, &CollidingEntities), With<Sensor>>) {
for (entity, colliding_entities) in &query {
println!("{entity} colliding with {:?}", colliding_entities);
}
}
Yup! Entity is set up like this:
pub fn create_user() -> impl Bundle {
(
UserInput::default_input_map(),
User::default(),
CollisionLayers::new(FresnelPhysicsLayer::Null, FresnelPhysicsLayer::Usable),
Collider::sphere(2.0),
CollisionEventsEnabled,
CollidingEntities::default(),
Sensor,
)
}
which is added as the direct child of a RigidBody::Dynamic (Tnua character controller)
And then the system reads like this:
fn process_use(
mut user_query: Query<(Entity, &mut User, &GlobalTransform, &ActionState<UserInput>, &CollidingEntities)>,
usable_query: Query<(&Usable, &GlobalTransform)>,
spatial_query: SpatialQuery,
mut commands: Commands,
) {
for (user_id, mut user, user_global_trans, action_state, colliding_entities) in &mut user_query {
let mut best_angle = 360.0_f32.to_radians();
let mut best: Option<(Entity, &Usable)> = None;
println!("User found: {}", user_id);
println!("{:#?}", colliding_entities);
// Remainder omitted for brevity
}
}
println #2 reads as an empty EntityHashSet, even once the sensor overlaps the target RigidBody::Static.
There might be something else wrong with my setup, but I've checked with inspector-egui and everything has the right components and collision layers as far as I can tell. I might try replicating with a minimal scene and see if I get the same result.
Do static rigidbodies not trigger sensors by default like with Jolt?
Static body: memberships 33, filters 0
Sensor: memberships 2, filters 32
If the static body's filter mask is empty, that would mean it can't interact with anything
It should default to LayerMask::ALL, or 0xffff_ffff
Ahhh so memberships and filters have to be mutual then
GOT IT, I created it with bevy_skein
so it defaulted to 0
that would explain it
Yeah a membership of A needs to be in the filters of B and a membership of B must be in the filters of A
The actual bitmask test is
(self.memberships & other.filters) != LayerMask::NONE
&& (other.memberships & self.filters) != LayerMask::NONE
That's different from what I'm used to with Jolt. Or at least I'm pretty sure it is. And sure enough it's documented right at the top of CollisionLayers. Appreciate the help Jondolf
No worries! It's the same in Box2D (source) and Rapier (source) and also PhysX IIRC. Jolt's setup seems a bit more complicated so I can't immediately tell how it works 🤔
Ah Jolt does also have this same group and mask setup built-in
But it also supports other filtering options
Huh. I wonder then if it's the way either godot-jolt is set up, or Godot is set up then
Or maybe I'm just misremembering
Godot has 32 layers and masks too, but it might be that it only requires one of the bodies to be in the other's mask rather than both matching
That's what I'm thinking, because I'm used to the filter mask being 0
Again i could be misremembering but I'm pretty sure that's how it works, I've used Godot for something like 5 years now. Given how many people show up here from Godot, I wonder if it'd be worth adding a "for Godot users" at some point.
Hi i'm new to avian3d and want to learn about how can i make ragdoll physics, is there anyone worked on this ?
I think I misinterpreted this
I assumed that local_point1 would match up with entity1
I believe local_point1 is always the leftmost entity, regardless of the ordering within collisions.get(e1, e2)
ah I see
yknow I assume way too much. I realize now that contact_pair has collider entities inside.
A PR for the benchmarking CLI
https://github.com/Jondolf/avian/pull/753
Haven't done too much CLI stuff with Rust so this was fun
clap is so nice
okay Rust workspace rules are dumb
I can't have benches in the same workspace and manually set the avian feature flags used for just benches since I believe the enabled features are shared between workspace members
guess I'll just not have it in the workspace then
kk it seems to work now, back to jamming 
swallows
"Hey have you been working on anything?"
"Oh yeah, 2 sec, I have a small change..."
Rewrites codebase
This one isn't too big, I have plenty of 2k+ line PRs
The diff here also includes quite a lot of Cargo.lock changes since we commit that to the repo now... Tbh I feel like I might revert that, I don't like Cargo.lock in diffs like this and I keep also getting minor merge conflicts because of it
You should see me game's 0.14 -> 0.16 changes
It' s not even done and we're doing 91 files, +3k, -14k
what's that -14k from 👀
Sdf collisions and rollback no longer in my game's repo
And a bunch of impl Components to the short derive style to set hooks
Haven't ported any logic to shorter styles like required components yet though
Anyone has miltiple fixed joints code example? 2D
spawn multiple entities, one for each joint
and then just
commands.spawn(FixedJoint::new(anchor, joint).with_local_anchor_1(Vec2::X * 100.0));
for each? I also do not understand what local anchor is supposed to do oh I get it, it's placement relative to the anchor
allright, got it working...kinda. For some reason it rotates the anchor intity 3 times clockwise.
Upd: oh it because my anchor is Dynamic rigid body. But I need it to be...
Ok so this is a weird one...
a rigid body that acts like a sensor on one collision layer, and yknow, normal-like on another
ive searched for 5 minutes so thought id ask for advice on how to do this, does anyone know?
seems like i might need to do something not-out-of-the-box for this to work
I'm debating making two child elements
so, two colliders 😆
yeah essentially LOL, just uh, I'm not sure why that made me feel gross about it
I guess that's probably the move
I'm a fan of simple-and-slightly-duplicated over complex
does anyone has suggestions why do small dynamic rigid bodies go through another rigid bodies? (no gap between orange lines)
UPD: nvm, I'm dumb - I had marker component that spawns particles on the floor as well
might be a scale issue, how big are they and what's the physics unit
^ this is why walls in videogames tend to be thick
Helps a lot with depenetration. If your units are small enough you'll probably observe this in most physics engines. CCD can help but won't necessarily fix the issue.
nvm, I'm dumb - I had marker component that spawns particles on the floor as well
qq -- is there any simple way to run 2d physics on the xz plane? have my own solutions (and could just translation lock the y axis with 3d) but thought i'd check here
(this is not for jam so no rush or anything)
#1124043933886976171 message
TLDR: possible yes, with a custom SyncPlugin, but no easy built-in support atm
understood. that makes sense to me. thanks! (a custom SyncPlugin is more simple than I expected!)
appreciate teh swift response 🙂
I will probably not do things this way, but it's good to know
Yeah if you were to do it, it's in this file
https://github.com/Jondolf/avian/blob/main/src/sync/mod.rs
I think you'd mainly copy everything, except modify the position_to_transform and transform_to_position systems to map Y to Z and vice versa
Hmm it's possible you'd also need to change the ColliderTransformPlugin (in this file) to handle some propagation logic slightly differently, at least if you have colliders in hierarchies
ah, hard to tell without diving into the systemset ordering + the logic more. OK. good enough of an answer for now though, thank you
maybe we can talk it out more after jam, I have the itch for 2d game + 3d cam often enough but still poking at the easiest way to handle it
any idea why particles are trying to push on the walls instead of just collect in one place? it's like they are avoid the emitter, even if linear velocity is 0.0
Hi. I am testing avian3d and I can't get objects to stabilize. Everything has friction but some things keep on sliding/rolling. Is there anything I am missing?
Hints or links to blog posts / videos on this topic would be welcome.
Could you add the PhysicsDebugPlugin to visualize the colliders?
It almost looks like there's some invisible static collider inside the container pushing everything out 🤔 but idk why it'd be there so probably not
Are you applying any forces or changing velocities somewhere?
What shapes are the colliders and how large are they?
adding some LinearDamping and AngularDamping can help slow things down and make things stabilize a bit better sometimes, though it shouldn't be necessary in most cases
The 2 cuboids keep sliding and the ball keeps rolling
the floor is static with friction 1.0
the rest has friction 0.3
It's expected that a ball doesn't stop rolling with friction, friction is what makes the ball roll in the first place, as opposed to sliding. To slow down rolling, you'd need rolling resistance, which is a separate phenomenon and not currently supported. But you can kinda emulate it using AngularDamping (though it'll also get applied when the ball is in the air, unless you set it based on whether the ball is grounded)
Based on the sizes of the axes drawn by the debug rendering, your objects seem to be quite small, which might partially explain the cuboids behaving weirdly
You could try setting the PhysicsLengthUnit to better match the sizes of your dynamic objects and hopefully improve behavior a bit, or just scale your object sizes up
A units-per-meter scaling factor that adjusts the engine’s internal properties to the scale of the world.
My units are in meters, as bevy expects?
And thanks for the quick response!
Anybody here making a pinball game using avian?
In fact my main problem is this: 3 cuboids joined with FixedJoint. When I put them on the floor they keep sliding.
Damping helps, but it's unclear if that is supposed to be a 0.0-1.0 value or more
In this case friction should do it's job?
IIRC the debug rendered rigid body axes have a length of 1 by default, which implies that for example that ball is closer to something like 0.05 or 0.1 in diameter (just estimating here). Most engines are tuned to behave best with dynamic objects being roughly in the [0.1, 10] range. But yeah it's still large enough that it shouldn't be too bad, and you can use the PhysicsLengthUnit to adjust the scale expected by the engine
I'm trying to model a pinball table of 0.5 m * 1.0 m with the ball diameter being 0.027 m
It's the exponential decay constant, typically in the range of 0.0-1.0 but can also be higher
Yeah I've seen a few similar cases where joints cause things to drift on the ground 🤔 I'm not exactly sure why that happens, but I'm guessing it might be related to how the current XPBD joints interact with the impulse-based contact solver
We're reworking the joints to also use an impulse-based solver, which should hopefully fix it, since I haven't seen other engines with that approach having this issue
Is there a reason you need to use FixedJoint? You can also attach multiple colliders to a single body by just adding them as children
the examples tend to spawn joints as new entities, but it's not exactly clear to me why - is there any potential complication from just adding joints onto one of the connected entities so it can be tracked easier?
Oh, that makes sense. Let me try that instead.
is it possible to limit the length of a raycast?
There should be a max_distance argument on the methods
cool thank you
I vaguely recall that originally it wasn't supported because of weird query conflict reasons, but I don't think that applies anymore? So we could probably support it now
There shouldn't be any fundamental reason you shouldn't have joints on the body entities, but:
- In general, I think it's clearer to model joints as their own entities. A hinge isn't necessarily a property of a door or the door frame, but it's something that connects them.
- If you want a body to have multiple joints of the same type, they need to be on their own entities, because you can't have more than one of each component on a given entity. This is also one of the reasons why we can't make joints be relationships.
- Using a separate entity for the joint lets you add its own
Namefor it, which might be nice for inspector stuff. - In the future, we could maybe use retained gizmos for more efficiently debug rendering joints. This might need them to be their own entities.
the specific case I'm working with is a chain, so intuitively I feel like each link "is" a hinge of its own, just with a visual component. but my bigger concern is just one of tracking - like if I despawn one entity involved in a joint does the joint despawn? I guess I can just make it a child of the chain link safely, right?
You should be able to make it a child, yeah. Right now joints aren't automatically despawned, there can also be rare scenarios where you want to detach an entity but keep the joint alive to later reattach it, either to that same entity or some other one
Or if you make it a child and despawn the parent, it will be despawned yeah
that feels intuitive enough, thank you for your detailed responses :)
Any reason this turns when it falls? And it always turns to the same side. A simple cube just falls flat.
is it a fixed joint or a set of child colliders
Childs to the center box
Hi, got some silly questions. is it possible to get the area of a collider, and what unit is ExternalImpulse, is it in newton?
it's in Δv/m, technically the units are unspecified
uh, do you know how do i convert newtons into that? idk what that is
if your length and mass units are meter and kg, i think it's in Ns? or N/s?
yes it is, many thanks!
how would i make something like this, where each of the red dots is a rotational joint connecting the limb to the main body? i've already had a few attempts but they've all gone so wrong. when spawning in the limbs, do i need to manually calculate the position, or should setting the local anchors be enough?
trigger.body is giving me the rigid body of the trigger.target, not the rigid body of the trigger.collider like the docs suggest
from a Trigger<OnCollisionStart>
found someone else ran into this issue: #1372394661171433552 message
i have two colliders in 2d that are overlapping (two platforms basically) and if i run a ball collider over the overlapping portion fast enough the ball will pop upwards. any ideas why?
ah i see there's a fix for this merged, but no 0.3.1 yet: https://github.com/Jondolf/avian/pull/752. i will patch to main in the meantime
ghost collisions happen any time something passes the edge of a collider, try messing with CCD & physics unit scale, but best bet is to make the colliders not have that type of junction
@vestal minnow patched it right after we ran into it together while working on the jam haha
Got some weird instances of the NPC supposedly hitting itself 
feature
hey! qq: I am setting up a scene in an in-game editor involving physics props. in this edit mode, i have physics paused.
I realized that I can't use any of the spatialquery stuff without the physics running for at least a frame, and was wondering the best way to get around this
aha, looks like update_pipeline might be what I'm after
edit: yep! classic post then immediately figure it out moment
is there a way to prevent an entity's collider from interacting with other specific entities rather than using an entire collision layer?
A trait for user-defined hooks that can filter and modify contacts.
thanks, i'll check that out
when i'm checking the pairs, do i have to check "both ways" in a collision pair?
i tried having them barely touching to but it doesn't make a difference
i am not sure how to push two platforms together and not have a weird collision
oh, guess i just needed to make the speculative margin smaller lol
what do you mean by "not have the colliders have that type of junction"?
if they have to move apart it'll be a little harder, but something like extending the internal edges of an inside corner to be further from the surface
wait i think i said that wrong
i have two platforms that are next to each other but they are static otherwise
that doesn't look very static
lol it happens if it's static too
in 2d you might be able to circumvent it with 2d capsules
will try
this did not work
oh wait
it did work, i just had to stop them from overlapping
that's strange, but thanks
i pretty consistently get two Trigger<OnCollisionStart> for one entity, i only want to react to one of them, is there a reasonable way to deduplicate these?
it's probably genuinely hitting two colliders in one frame, but that's...fine, just bothersome
store whether you've reacted in a local or component
is it possible to define an offset for a Collider position ?
cherry-picked the patch now ^
but main has better performance 😉
You can add the Collider as a child of the body entity, and apply a Transform to that
Another way to do it if you want to keep it on the same entity is to use Collider::compound with just a single shape and offset that
I see, muchas gracias !
I'm inserting the Collider as a required component , so i'll try the Collider::compound solution 👍
by chance is there any work done on the ability to save colliders from a collider constructor? seems like this could be possible if you can extract the parry collider
I think the colliderconstructor caches colliders on main... is that what you mean?
I mean the ability to save and load collider constructors...I might try doing this if I finish my lil game jam thing early as a nice-to-have :)
so ill see above fixing that
@visual sparrow it is faster...I think it might just be the model 😄
that's a lot haha
would it be bad for optics if you just made them all sphere colliders?
Totally
Smart
Also btw I will be forking wasm-bindgen, not only for this hanabi stuff, but actually for work purposes
Will have help until maintainers come back
what features do you need?
Or did you run into a bug?
Oooh probably the thingy you mentioned before
Several yeah
Well I'll be interested in using your fork then 😄
Spheres are the simplest of the 3d colliders right? Over cuboids? Not sure how aabb3d would work. Intuitively, seems spheres would be simplest
Ok I might be wrong, but from the docs I would have interpreted that OnCollisionStart the collider and body both refer to the "other" entity colliding with the trigger.target().
But from my tests, it seems the collider is indeed the "other" collider, but body is from the trigger.target() entity.
https://docs.rs/avian2d/latest/avian2d/collision/collision_events/struct.OnCollisionStart.html
Is my reading comprehension off from the docs, are the docs wrong or might this be a bug?
A collision event that is triggered for observers when two colliders start touching.
Spheres are the cheapest, yes
Fixed on main 🙂
Run cargo update, it should be fixed in 0.3.1 which I released earlier today
(or use main)
i'm running my sim headless - no rendering. is there a way for me to run the physics at a constant timestep (125ms, in my case) but to do so as fast as possible? i see with_relative_speed but that warns of accuracy issues, and i don't want a specific relative speed i just want to not wait at all between each run of the FixedMain schedule. so like if i wanted to collect data for 2400 timesteps it would take <however long my cpu takes to do this> rather than exactly 5 minutes
this might help https://docs.rs/avian3d/latest/avian3d/schedule/struct.Physics.html
specifically this snippet
To advance the simulation by a certain amount of time instantly, you can advance the Time<Physics> clock and manually run the PhysicsSchedule in an exclusive system:
use avian3d::prelude::*;
use bevy::prelude::*;
use core::time::Duration;
fn run_physics(world: &mut World) {
// Advance the simulation by 10 steps at 120 Hz
for _ in 0..10 {
world
.resource_mut::<Time<Physics>>()
.advance_by(Duration::from_secs_f64(1.0 / 120.0));
world.run_schedule(PhysicsSchedule);
}
}
beginning to think it's got little to do with avian and probably more to do with observers
i mean the step time isn't fantastic here but I'm guessing the takeaway here is "avian is consuming less than 1/10th the time of this frame"
Did you try generating a flamegraph?

no
gotta run tracy with windows aagh
tomorrow me's problem
you can also use trace_chrome with something like perfetto
kind of annoying and janky but it works on linux
No need, use cargo flamegraph
Check the profiling guide
gah alright
It’s much simpler than getting tracy to do what you want 🙂
the profiling guide's all about tracy and lowkey I kinda wanna see what that thing can do 😆
but ur right
Tracy is better when you already know which function you care about
If you need to explore what even is the bottleneck, a flamegraph is the better tool imo
Hey uhmmm… Just wanted to hear if anyone knows what to do if your program is so fast that the startup of the program dwarfs it so much, that you can’t actually see the program part that you wrote in the flamegraph…
Like, is there any program I can view it in?
well
the flame graph sure is fat
the only thing I can determine is 1. it's in the update schedule
2. it is called
LOL
i'll give this a shot thanks
Open the svg in your browser 🙂
You can click the parts then
Is there any easy method to create some type that attracts rigid bodies to itself?
i.e. something that can exert a local force on all bodies within a collisionlayer
I imagine I could just apply an external force in the direction of all objects that exhibit this property, but was wondering if there's a better way
edit: yeah that worked really well
^I think this question was asked before and Jondolf gave exactly this recommendation
There's no analogue to Godot's wind/gravity volumes in Avian. Honestly I think it's best to keep it that way too, everyone wants something slightly different for their custom forces.
this won't work for me because i need to run all of the rest of my systems between each physics timestep.
any other ideas for how to decouple the physics timestep length from the other clocks? i feel like this might be useful generally.
well maybe it will work - if i put the manual physics advance code in a system and run it in every Update
i'll give it a shot
sounds like you just need like, an uncapped framerate but stepping the physics by a fixed amount each frame instead of based on real time - maybe it's as easy as just pausing the physics clock and advancing it a fixed amount yourself?
you might be interested to see the details of the default physics schedule runner: https://github.com/Jondolf/avian/blob/main/src/schedule/mod.rs#L197
in particular, the physics schedule is only run on non-zero time deltas, and the physics time is only automatically advanced by real time if unpaused
yeah i def need to understand how the physics schedule runner works. thanks for that link
i wonder if setting the relative time of Time<Real> to something very high (rather than changing it for Time<Physics>) would work - that way every run of Update would cause very many runs of FixedMain to catch up
oh yeah i forgot even if you run uncapped the number of FixedMain is still, well, fixed
and if you have systems running in fixedupdate that you need between each physics schedule run then you still need fixedmain to run too
yeah my idea is if i tell FixedMain that 5 minutes have elapsed (even tho in fact no real wall clock time has passed) then it'll run all of my systems 2400 times in a row
if all you need is fixedupdate systems to run between each physics step then that sounds reasonable enough to me
i have systems in all of the Fixed- schedules but i think they'll all be run, yeah
gonna try it out now will report back
if that's too jank for any reason you might just be able to use an exclusive system that advances the physics clock, runs fixedmain, then runs physicsschedule
if the fixed main runner is to be believed it may be that simple https://github.com/bevyengine/bevy/blob/main/crates/bevy_time/src/fixed.rs#L239
advancing the clock doesn't seem to work but setting the relative speed of Time<Virtual> does
it doesn't run quite as fast as i expected but i guess it does have to do all the number crunching
takes about 14 seconds to run thru around 6 minutes of simulation
i'll take it
🎉
thanks for your help!
sure thing!
this was in a debug build with glam assertions enabled 🤦♂️ - in release w/o assertions it takes about 2 seconds 🎉
@vestal minnow So I want to use Avian for collision detection only - no velocity or gravity calculations, I want to do them myself because it's a 2D wrap-around world. Is Avian even the right tool for this?
Let's say I just make everything kinematic, am I wasting the power of Avian?
It'll have some amount of extra overhead over doing everything manually, but it should work
For just pure geometric queries (cast a ray against a given shape, compute contacts between two shapes, etc.) I would probably use Parry directly, you'll just have to deal with Nalgebra if you go that route
and it doesn't have a built-in broad phase or a particularly convenient API for querying with acceleration structures (the Qbvh in Parry) so you'd have to set those up yourself if you need them
Well, what I get from Avian is debug mode and integration with ECS
does avian provide any built in gizmo implementation for viewing collision or will I have to do that myself?
the alternative is using parry directly as jondolf says, i did that for my last game and it was pretty annoying having to convert between completely different formats. i wouldn't worry about "wasting the power", it'd be better than using parry directly imo
also i managed to run into performance issues in a game with like ~100 colliders. no broad phase is rough
^I feel like in Bevy of all engines it's really easy to peel back layers of abstraction when you need to, compared to other engines. If your intuition is coming from other engines/frameworks where it can be hard or impossible to use a library/plugin piecemeal, that's not really the case with most Bevy plugins.
I feel like that's particly because bevy, bevy plugins, and bevy games, are all made the same way, like, I'm doing research into a 3D Radiance Cascade based raytracer which isn't something I wouldn't even consider in Godot since Godot Engine, GDExtension Plugins, and Games are all at different levels of abstraction
also some godot plugins are C#, or GDScript, or C++ or rust or whatever the developer decided to use
This was one of my key factors in trying to switch my current project over to Bevy. Our studio is planning to make very similar games, all first person shooters, and it felt... Like it was going to be a maintenance nightmare to maintain, y'know, a Godot fork, a GDExtension in some arbitrary language (well, except C# for some reason), then scripts in C#, with some third party stuff in GDScript. I'm the only programmer and I don't want to juggle 4 languages when most of our code is gonna be reusable.
I'm considering a combo of bevy_ecs_ldtk and avian2d to try making my first ever attempt at a platformer. for cases where I have a large rectangular region of terrain that has lots of visual variation for flavor but is all ultimately "different kinds of dirt", I assume I'll have a much faster physics phase if I can make fewer colliders that encompass as much of the terrain as possible, rather than each tile block naively having its own.
Do either of these crates make it easier to accomplish that kind of optimization? Directly handle that for me? or is there some kind of preprocessing step I can do that has a well known implementation style?
a rephrasing of what I mean is, if I have consistently-sized stone block tiles in a 2x2 or 3x3 square, do I need to hand write something that just makes a 2x2 / 3x3 collider with the correct outer corners?
keep in mind that upgrading bevy versions is also a huge pain
Yeah I'm aware. I'd rather have to upgrade between versions than maintain a bunch of different languages in the same codebase. I know they're just different devils but isn't that what engineering is all about?
can i make a sphere that attracts objects? like make it have gravity of its own
someone seemed to ask something similar up above #1124043933886976171 message
thanks
What might be the best way to get velocity of colliding bodies at the time of collision?
I thought CollisionStarted would be where I could still query for LinearVelocity of the entity and get an accurate "pre-collision" velocity, but am I mistaken about this? The reason I'm asking is because velocity seems to be all over the place when I query for linear velocity here.
i'm pretty sure CollisionStarted is after the first tick of a collision
Hi, i am trying to make an buoyancy simulation. I have an 1000 kg and 1m³ cube but it bobs up and down instead of standing still. anybody having the same problem?
sounds like you need some kind of damping
probably just save velocities before each physics step, then grab the saved velocity if a collision starts
Hmm this is a fine enough workaround tbh! Thank you for the idea
is there any way to make a collider from a bevy primitive?
kinda sucks having to rewrite code like this :(
Collider::from(my_primitive)
or alternatively my_primitive.collider()
it's not supported for all of Bevy's shapes atm, but it should work for the common ones
awesome thank you guys
i've got a utility fn collider_mesh<T: IntoCollider + Into<Mesh> + Clone>(t:T, &mut Assets<Mesh>) -> (Collider, Mesh3d)
noticed my simulation doesnt seem to consider the local_anchorx on my DistanceJoint even though I'm certain its being set correctly. any common issues people run into with it? everything is where it should be when i debug with gizmos, but the joint is definitely still being considered from the center of both objects
DistanceJoint::new(entity_a, entity_b)
.with_local_anchor_1(wire_offset)
.with_local_anchor_2(offset)
.with_linear_velocity_damping(0.05)
.with_angular_velocity_damping(0.05)
.with_rest_length(rest_length)
.with_compliance(0.005),
your compliance seems quite high, have you tried reducing it
I couldn't find an example and the docs don't really seem to explain it, to be able create a polygon of arbitrary shapes, should I use convex_decomposition and just feed it the whole polygon, or how does it work?
what data do you have and what sort of result do you want?
A 2d polygon basically! Which I'll create in inkscape and then convert from svg to a vec of vec2's. That is the plan anyway :)
My plan for a quick ad hoc level editor for the jam 😄
Maybe just a polyline is fine, I don't know! The docs are very terse regarding descriptions on these
do you expect it to ever be concave?
Yes
A triangle mesh.
Fantastic, thank you!! I tried going through all docs but couldn't spot that
oh, though collider seems like it'd treat it the same as a polyline anyway so it might be irrelevant
probably worth testing
@vestal minnow I seem to have found myself creating a duplicate type for my collider to get a version without unnecessary sacling ... Makes me wonder if we should just seperate the original collider, and the scaled one into seperate components like you once mentioned 🤔
Also getting a bunch of warnings and panics when diagnostics aren't enabled, which doesn't like particularly desirable behavior 🤣
^
You mean the debug plugin? 🤔
is physics supposed to slow down and behave weirdly on lower fps? on 144fps it's fine and on 60fps linear velocity isn't applied properly to transform
we don't change transform/position by hand
To clarify (we're working on the same project): we only ever mutate ExternalForce & ExternalTorque, and yet we see differences in speed depending on the game's FPS. Any idea what could cause this?
In which schedule do you mutate them, and where does avian run?
Figured it out:
If both in FixedX there should be no difference if either or both aren't in the Fixed schedules it's not surprising
Yeah that was it 😅 I was using forces.persisent = false and torque.persistent = false, and updating my forces in Update instead of FixedUpdate
As a result, at low FPS multiple physics updates happened per frame, and the forces weren't applied during the subsequent physics updates
i have a Sensor that works as I'd expect when by itself, but when I make it a child of something with another collider on it it stops working. is that expected?
Any chance there might be a bug in avian in relation to observers and despawning?
thread 'main' panicked at ...crates/avian2d/../../src/collision/contact_types/contact_graph.rs:401:18:
swapped entity has no entity-to-node mapping
stack backtrace:
0: __rustc::rust_begin_unwind
1: core::panicking::panic_fmt
2: core::option::expect_failed
3: core::ops::function::impls::<impl core::ops::function::FnMut<A> for &mut F>::call_mut
4: bevy_ecs::observer::runner::observer_system_runner
5: <hashbrown::map::Iter<K,V> as core::iter::traits::iterator::Iterator>::fold
6: <core::iter::adapters::cloned::Cloned<I> as core::iter::traits::iterator::Iterator>::fold
It happens rarely when I despawn everything.
is there any way to query or check for a specific collider?
I want to utilize the diameter and half height of a capsule collider but that's just not something everything has
fn tocap(c:&Collider) -> Option<&Capsule3d> { c.shape().downcast_ref::<Capsule3d>() }
is there a way to make the rays ignore the parent entity, because this, is not working
pub fn is_on_floor(
mut q_parent: Query<(&mut entity::CharacterGravity, &Children, Option<&physics::IsGrounded>, Entity)>,
q_child: Query<(&RayCaster, &RayHits)>,
mut commands: Commands
){
for (mut gravity, children, grounded, entity) in q_parent.iter_mut(){
let mut new_pos = Vec3::new(0.0, 0.0, 0.0);
let mut has_hit = false;
let mut hit_count = 0;
for child in children.iter(){
if let Ok(cast) = q_child.get(child){
let hits = cast.1;
for hit in hits.iter_sorted(){
if hit.entity == entity {
has_hit = true;
hit_count += 1;
hit.normal;
break;
}
}
}
else{
continue;
}
}
if grounded.is_some() && has_hit == false {
commands.entity(entity).remove::<physics::IsGrounded>();
}
else {
commands.entity(entity).insert(physics::IsGrounded);
}
if new_pos != Vec3::ZERO{
new_pos /= hit_count as f32;
gravity.ground_pos = new_pos;
}
}
}
it depends on what you're using.. I usually use SpatialQuery which has a filter you can populate with entities to ignore
https://docs.rs/avian3d/latest/avian3d/spatial_query/struct.SpatialQuery.html#method.cast_ray
I don't think the cast_ray on the Collider struct has that iirc 🤔
A system parameter for performing spatial queries.
I'm using the raycaster and just trying to get the position of the first, non player, that it hits
yeah i think the SpatialQuery is a bit nicer to deal with here with the excluded entities. I'm not sure how else you could get around this without using CollisionLayers.
oh that was late whoops
oh it appears there's also excluded_entites on the SpatialQueryFilter struct which is in your RayCaster component i believe
A component used for raycasting.
Rules that determine which colliders are taken into account in spatial queries.
Hey all, I am currently implementing a sensor collider that is indicating for me when another entity is colliding with it and finds the nearest one to add a marker component to it, so when I press "e" I could later pick them up if they are still in range,
it currently looks like this and it works, but I don't know if it's as optimized as it can be, or if there a better way to do this maybe with https://docs.rs/avian2d/latest/avian2d/spatial_query/struct.SpatialQuery.html#method.shape_intersections
A system parameter for performing spatial queries.
Currently looks like this:
fn detect_pickable_range(
mut commands: Commands,
pickable: Single<(Entity, &GlobalTransform), With<PickableRadius>>,
collisions: Collisions,
items_query: Query<(Entity, &GlobalTransform), (With<Weapon>, Without<PickedUp>)>,
marked_items: Query<Entity, (With<Weapon>, With<PickupMarker>)>,
) {
let (pickable_radius_entity, radius_centre) = pickable.into_inner();
let player_pos = radius_centre.translation().truncate();
// Collect colliding items with their distances in one pass
let colliding_items: Vec<(Entity, f32)> = collisions
.collisions_with(pickable_radius_entity)
.filter_map(|contact_pair| {
// Determine which entity is the item (not the pickable radius)
let item_entity = if contact_pair.collider2 == pickable_radius_entity {
contact_pair.collider1
} else {
contact_pair.collider2
};
// Check if it's a valid item and calculate distance
items_query.get(item_entity).ok().map(|(entity, transform)| {
let distance = player_pos.distance(transform.translation().truncate());
(entity, distance)
})
})
.collect();
if colliding_items.is_empty() {
// No items in range - remove all pickup markers
for marked_item in marked_items.iter() {
commands.entity(marked_item).remove::<PickupMarker>();
info!("Removed pickup marker from entity {}", marked_item.index());
}
} else {
// Finds the closest item
let (closest_entity, _) = colliding_items
.iter()
.min_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal))
.unwrap();
// Remove markers from all marked items except the closest
for marked_item in marked_items.iter() {
if marked_item != *closest_entity {
commands.entity(marked_item).remove::<PickupMarker>();
}
}
commands.entity(*closest_entity).insert_if_new(PickupMarker);
info!("Added pickup marker to entity {}", closest_entity.index());
}
}
Would appreciate any input!
I'm trying to do something similar to the "conveyor belt" example from the avian3d crate, and I'm getting some odd behaviour
https://github.com/bravely-beep/beepcraft
The player capsule seems to move in one direction when it hits the ground, then reverse and move backwards?
...I must have been doing something wrong because I can't reproduce the bug now lol
Okay, here's a more minimal reproducible example
Turns out it has nothing to do with physics hooks
Dropping a rotation-locked dynamic capsule rigidbody onto a moving surface causes it to slide in an unrelated direction when it lands
Looks like it might be the same thing as #754
Changing the player collider to a cylinder gets the same result
But changing it to a cuboid causes the slide to happen in a different direction
And a sphere gets different directions again depending on its radius
I'm working on a top-down 2d game that I'm hoping will have procedural generation for the levels (something like the inside of a cave system). I'm currently playing with avian2d, hoping to use it for both ray casting for line of sight as well as collisions with the level geometry. My question is if people have suggestions for how to generate the 2d meshes to work best with avian? Should I aim for a closed mesh that the player is "inside of" or an open one that they are "outside of"?
seems generally like being outside of colliders is the expected situation, but trimeshes also don't seem to have differentiable sides
to get my sim to run very quickly i run this system in PostStartup
fn go_fast(mut time: ResMut<Time<Virtual>>) {
time.set_relative_speed(1000.0);
}
as written it works, but if i increase it to anything more than ~1000.0, the physics gets real weird. i'm surprised by this because i'd expect FixedMain to just run some number of times to catch up to its "lagging behind" the Virtual clock but otherwise not be affected at all by any increase in that clock's speed. reckon it's because Time::overstep overflows?
Avian2d's Trimesh collider does seem to be doing something odd with my 2d mesh. It seems to be creating a collider (or at least reporting a ray intersection) along the x=y line, whether the ray is being cast from "inside" or "outside" the mesh.
Oddly its also doing so if I use a Polyline...
In what system set is LinearVelocity applied?
I couldn't find the system that uses it
I'm trying to fix a jitter that happens when I make the camera follow the player (which moves with LinearVelocity) and suspect it might be a system ordering thing
Ok, this is just odd now. Even if I use a plane old rectangle, 300x300, with a rectangle collider, same shape, SpatialQuery::cast_ray is still only reporting hits along the x=y line, even where nothing exists to collide with. Am I doing something stupidly wrong here, or is this a bug?
Integrates position and rotation based on the given velocities in order to find the position and rotation after delta_seconds have passed.
ie https://docs.rs/avian3d/latest/avian3d/dynamics/integrator/enum.IntegrationSet.html#variant.Position
System sets for position and velocity integration, applying forces and moving bodies based on velocity.
System sets for the main steps in the physics simulation loop. These are typically run in the PhysicsSchedule.
i put most of my systems .after(PhysicsStepSet::Last)
maybe the bug is in the way you're extracting the hit position?
Vec3::ZERO + 6.0 gives (6,6,6)
This is my code: ```rust
let max_distance = elapsed * 200.;
let solid = false;
let filter = SpatialQueryFilter::from_mask([GameLayer::Level, GameLayer::Monster]);
for i in 0..99 {
let dir = a.slerp(b, i as f32 / 100.);
if let Some(hit) = spatial_query.cast_ray(center, dir, max_distance, solid, &filter) {
let loc = center + (dir.normalize() * hit.distance);
let rounded = Vec2::new((loc.x * 10.).round() / 10., (loc.x * 10.).round() / 10.);
// println!("Inserting {rounded} from {}", hit.entity);
quadtree.insert_point(rounded);
}
}
Where `a` and `b` are two directions and center is the player's location.
(I don't know why RayHitData doesn't give the location of the hit. Seems like that would be useful. But that's neither here nor there)
did you mean to put loc.x in there twice
just do loc.map(|v|(v*10.).round()/10.)
or even (loc*10.).round()/10.
o7
Thanks
How to migrate from 0.2 to 0.3 for the following use cases? ColliderParent and Collision are not found
mut entities: Query<(&ColliderParent, &GlobalTransform, &Collidable)>,
mut collision_event_reader: EventReader<Collision>,
mut entities: Query<(&ColliderOf, &GlobalTransform, &Collidable)>,
mut collisions: Collisions,
and use collisions.iter()
(or collisions.get(entity1, entity2) if you want to get data for a specific contact pair)
What were you using it for? Each contact pair now stores some ContactPairFlags that have the same info (and more)
If you just want to know if the collision started/ended, there are collision_started and collision_ended helpers on the ContactPair
which just uses the flags internally
I was trying to only process a pair at most once
how do I get the position of a hit from a raycast?
the hit returns the distance, so you can always do ray.origin + ray.direction * hit.distance
although those are in local space so you might have to transform them
is there any existing means to transform it or will I have to do the matrix math?
divide by GlobalTransform
RayCaster has global_* functions if that's what you're using: https://docs.rs/avian3d/0.3.1/avian3d/spatial_query/struct.RayCaster.html#method.global_origin
A component used for raycasting.
I'm trying to use collision_start(), but it's always false for all ContactPair from the Collisions. Here is my code for checking the flags
fn collide(
entities: Query<(&ColliderOf, &GlobalTransform, &Collidable)>,
collisions: Collisions,
) {
for contacts in collisions.iter() {
info!("{:?} {:?}", contacts.flags, contacts.collision_started());
...
This prints out all collisions like this
2025-06-14T00:06:18.304998Z INFO game::play::collider: ContactPairFlags(1) false
2025-06-14T00:06:18.551886Z INFO game::play::collider: ContactPairFlags(1) false
2025-06-14T00:06:19.369841Z INFO game::play::collider: ContactPairFlags(1) false
2025-06-14T00:06:20.254262Z INFO game::play::collider: ContactPairFlags(1) false
2025-06-14T00:06:20.254285Z INFO game::play::collider: ContactPairFlags(1) false
2025-06-14T00:06:20.436256Z INFO game::play::collider: ContactPairFlags(1) false
2025-06-14T00:06:20.719383Z INFO game::play::collider: ContactPairFlags(1) false
2025-06-14T00:06:20.738482Z INFO game::play::collider: ContactPairFlags(1) false
Quick noob question: I recreated a floating character controller (with help of this video) using a sphere RigidBody and I move it around with linear impulses (only on XZ plane), but when I walk into a wall while falling I slide down it slowly. Adding Friction::ZERO component didn't seem to help.
(Also when I enable some code that's supposed to keep the body somewhat upright with angular impulses, it appears to stick to the wall outright, not even sliding down at all.)
Oh yeah here is the code if it helps: https://gist.github.com/copygirl/7ac13866e17d3dda75189b239a10e1ed
Feel free to judge the rest of it. I probably got some values / calculations wrong too.
I have no specific feedback, but the most popular implementation for Bevy is Tnua, so that will probably be a helpful reference 🙂
I've looked at it but it looks so confusing nor is it built for Avian specifically, I don't know if I want to go with it. I'm interested in building my own.
Is there a way to combine several compound colliders? I am building my meshes manually with Collider::convex_decomposition_from_mesh and realized I was getting several colliders per sceneroot. Which is fine, but I tried to combine them into a Collider::Compound and this fails saying that nested compound colliders are not supported. It feels like I'm missing something that I now have to cache a vec of colliders and spawn them as children rather than being able to combine them 🤔 but maybe this is the price I pay for not using ColliderConstructorHierarchy ?
Oh wait nevermind, I've done it like this.
let mut really_all_shapes: Vec<(Vec3, Quat, SharedShape)> = vec![];
for collider in found_colliders {
let shape = collider.shape();
match shape.0.as_compound() {
Some(compound) => {
for (p, c) in compound.shapes() {
really_all_shapes.push((
p.translation.into(),
p.rotation.into(),
c.clone(),
));
}
}
None => really_all_shapes.push((
Vec3::new(0.0, 0.0, 0.0),
Quat::IDENTITY,
shape.clone(),
)),
}
}
let new_collider = Collider::compound(really_all_shapes);
Colliders constructed from meshes are cached for you on main, not sure if this landed in a release yet
What’s the reason you’re combining the colliders into a compound collider? There’s AFAIK no performance gained by doing so
^ When using ColliderConstructor / Hierarchy
I'm not using any of the avian plugins, just putting colliders on stuff and doing manual collision tests because I felt like everything else was overkill for my usecase. So it's convenient for me to have a single collider to grab an aabb to put in spatial partitioning lol
You sure you don’t want to use the regular Avian workflow? You get cached colliders, a separate broadphase and narrowphase, and optimization for free
If not, you can check the cache impl if you want to replicate it. It’s basically a HashMap<Handle<Mesh>, Collider> 🙂
If what I'm working on was any more complicated I definitely would and will in the future, but I already have systems from previous projects where I cache colliders, do spatial partitioning, and broadphase/narrowphase collision and inelastic collisions. The only thing I didn't have was collisions between arbitrary collider shapes and contact manifolds, which has been quite nice to use on its own.
And optimizations, I do not have those lol. I have no doubt that it would help to switch over. But the burden of learning something new is too great right now
It turns out I have to add CollisionEventsEnabled marker to the collider to trigger the started condition or event being recorded
@vestal minnow I'm not sure if this might be a bug in the ContactPair::collision_started method, or the flags value is not updated properly. When I'm using the following code, I can capture the started event and get the collision contact pair (when collider has the CollisionEventsEnabled marker)
fn collide(
collisions: Collisions,
mut collision_event_reader: EventReader<CollisionStarted>,
) {
for &CollisionStarted(collider1, collider2) in collision_event_reader.read() {
let Some(contact) = collisions.get(collider1, collider2) else {
warn!("can't find collision when collision started");
continue;
};
But if I don't use the event reader, and fetch contact pair directly from the Collisions, the contact pair flags doesn't has the started flag set.
where's it scheduled?
Hmmm right so this is actually needed for internal reasons, it resets the STARTED_TOUCHING flag right after sending the event, so from a user POV it's never actually set. Should change the collision_started helper to not use that 🤔
Mm I could probably refactor the flags a bit to better expose to users whether the collision started/ended
maybe always reset STARTED_TOUCHING and STOPPED_TOUCHING in update_contacts instead of right after sending the events
could maybe even replace them with a WAS_TOUCHING flag I guess
The collide system is scheduled in PostUpdate if that is helpful
those 2 shapes are connected with fixed joint, why does the left one sink into the ground?
it stops when the right corner hits the ground
@vestal minnow
How big are the shapes? If you're using pixels, you might want to configure the PhysicsLengthUnit if you haven't already
It could also be related to the current joints behaving weirdly with contacts like in #1124043933886976171 message
multiples of 50 with default physics unit length
If your average dynamic object is roughly in the ballpark of 50 units, you might want to set the physics length unit to that, it just adjusts the engine's internal tolerances and stuff to better match your scene's scale and provide better behavior for it
the easiest way is just
PhysicsPlugins::default().with_length_unit(50.0)
or insert the resource manually
interesting, that fixes the sinking
the objects starts drifting sideways, but that's common in other engines
do you suggest using length unit around lower-sized objects or larger-sized objects?
It doesn't need to be too accurate, just something that's in the ballpark of what you estimate a common dynamic object's size to be
you can also just try different values and see what works best
I'm guessing the sinking here might be because the max_overlap_solve_speed in the SolverConfig is just 4.0 by default. It's good if you're working in meters, but for pixels it's just 4 pixels per second, which is pretty small
so it ends up resolving overlap really slowly unless you crank up the expected scale with the length unit
Also FYI @cerulean zephyr if you don't need any specific joint behavior, then you might get more stable results by just using a single rigid body and attaching the collider(s) as child entities
that creates a more rigid and efficient attachment since it doesn't need to solve any constraints for the joint itself, it's just multiple shapes attached to a single body
makes sense
yeah, i've had a rough read through all the features, I must have missed the unit length because it reminded me of simulation scale from rapier
all in all it's good that this came up in a very simple scenario
So imo Rapier is quite misleading here. Internally it does the exact same as Avian, meaning it just scales some length-based tolerances (as stated here), but bevy_rapier's docs say this
In this example, we could set
RapierPhysicsPlugin::pixels_per_meter(50.0)so that while all the transforms, collider size, positions, etc. are expressed in pixels on your end, bevy_rapier will automatically divide them by 50.0 whenever it inputs/reads data into/from Rapier itself.
which I'm 90% sure is just straight up wrong, it doesn't scale any transforms or colliders like that
However it does scale gravity, which is weird to me, since that just makes gravity use different values than if you were to manually implement gravity by just modifying velocity
but yeah the length unit in Avian is basically the same as the length unit in Rapier or Box2D, excluding that gravity thing in bevy_rapier (but not the core rapier...)
mhm
is there a possibility of a ColliderConstructor::Combined(Vec<ColliderConstructor>) ?
Related: https://github.com/Jondolf/avian/issues/328 (though not quite the same)
I believe so
We would just need to unpack nested compound colliders, as parry panics otherwise
But that's fairly simple
(Assuming that by Combined you mean Compound)
yes, that. thank you
I've got a very inconvenient question
is it possible to run avian2d and avian3d together?
I imagine it is, just a lot of name collisions which is fine
so maybe not-so-inconvenient. I thought for a second that both crates would reference the same memory but I doubt it
They're currently entirely separate crates, so I believe it should technically work, but I don't think I've tried it
what kind of mad science is cookin haha
In the long term I'd like to experiment with having a single avian crate that defines most of the user API for both 2D and 3D in a single crate, for example there'd be separate Collider2d and Collider3d types. Then avian2d and avian3d would just provide the actual 2D and 3D functionality for that API
This sort of design could work for "first-party" Bevy physics, with Bevy providing the core API, and backend crates (for Avian, Rapier, etc.) implementing the actual functionality
but that's for some day in the future, I'm focusing on just improving the core Avian for now :P
Hmm I've been frequently annoyed about the thing where Position and Rotation are not required components, and are only inserted at the start of the physics step, causing the entities to be in an invalid state for a while. But I just thought of a relatively elegant solution that might work? 🤔
- Add a
RecomputeGlobalTransformsparse-set component that has a lifecycle hook that immediately updates theGlobalTransformwith theTransformHelperwhen the component is inserted - Require
RecomputeGlobalTransformforRigidBodyandCollider, causing their global transforms to be initialized with correct values - Require
PositionandRotationforRigidBodyandColliderwithPLACEHOLDERvalues (to distinguish between user-defined values and default values), but use a lifecycle hook to immediately update them to match theGlobalTransform
The only problem is that idk if I can guarantee that the hook for RecomputeGlobalTransform runs before the Position and Rotation hooks...
I kinda need this to use hooks and not observers because some other lifecycle observers would like the correct Position and Rotation values, to e.g. initialize AABBs correctly
mm it'd be nice if the hooks were called in the order the components are listed in the require macro
however Bevy stores required components in a hash map, so I think the order is currently arbitrary
if we switched that to an index map... I wonder if it'd work the way I want
OH I can just use on_add for RecomputeGlobalTransform and on_insert for Position and Rotation in this case
@vestal minnow I'm currently experimenting with creating an avian-first navmesh generation crate similar to recast. I'm developing against Avian's main, so I'd like to ask if there's anything you know will probably change in the next weeks so that I know to look out for that API?
Also, I think you know first-hand my motivations behind that crate 😛
Some likely changes that come to mind
- Make
PositionandRotationrequired components so they're available right after spawn - Force rework: Remove the external force and impulse components in favor of other APIs, and significantly rework solver integration logic
- Broad phase rework: Switch the broad phase to use BVHs, also reworking how AABBs are initialized and updated
- After the broad phase rework, try to integrate @cinder summit's reworked spatial query pipeline stuff
roughly in that order
I'm trying to get the first one done today and also continue finishing up the force rework stuff
Thanks! Forces should not be relevant to me, but the rest is.
Does the Aabb work change how they’re accessed?
And are the BVHs exposed?
Ah, and do you mind if I pick the name avian_navmesh?
The BVHs are exposed, but I'm not 100% sure yet how I'll handle access to AABBs. During BVH traversal it indexes into a vec of "proxies" for fast access, which are intended to store the AABB and CollisionLayers, but I might also keep the ColliderAabb component around.
If not, the AABBs will be accessible anyway through a ColliderTrees resource. The colliders store a BroadPhaseProxyIndex component to let you index into the vec of proxies
(naming might change)
Ah, nice!
Nope, you can claim that
I've got some code that looks like
fn update_sight_rotation(
cursor: Res<CursorPosition>,
player: Single<(&Position, &mut Rotation), With<Player>>,
) {
let (position, mut rotation) = player.into_inner();
let diff = cursor.current() - position.0;
let diff = diff.y.atan2(diff.x) - FRAC_PI_2;
*rotation = Rotation::radians(diff);
}
The rotation component correctly reflects my cursor position, until, at some point, it stops completely. i.e.
does this appear to be a bug? I can make an MRE if you'd like. I've not seen an error or warning from avian2d
Smells like NaN poisoning. Could you print your rotation?
Notably, I don’t think this correctly handles the case when diff.x is 0
Nvm, that case should be alright
After looking at it for a while, I'm not sure where the NaN could come from 
If you create an MRE, I will take a look
i'm running a system in FixedLast (i also tried FixedFirst) in which i reset the translation of an object if it goes outside of a given volume. code looks like this:
pub fn reset(
object: Single<(&mut Transform, &mut Position, &mut LinearVelocity), With<Marker>>,
) {
let (mut transform, mut position, mut velocity) = object.into_inner();
if transform.translation.y.abs() > Y_BOUNDARY {
// transform.translation = Vec3::new(0., 2., 0.); DOES NOTHING
**position = Vec3::new(0., 2., 0.); // WORKS
}
}
any idea why using Transform doesn't work?
mutating the Transform works in other systems
IIRC the transform is overwritten at the end of the fixed step by Avian to correspond to Position + Rotation. If you're using interpolation (and you should!) then Transform is also touched in the variable timestep schedule right after.
that makes sense and a similar suspicion is why i tried putting it in FixedFirst instead. oh i wonder if interpolation is causing this, good point
i'll turn it off and see what happens
yep that fixed it
i think this qualifies as a bug in the interpolation plugin eh?
this also explains why this doesn't happen when i'm running my sim headless
i guess i can just run my system after TransformEasingSet::Reset in FixedFirst
yeah that resolved it, thanks for the help @visual sparrow
Nope, it’s a known limitation and documented somewhere
It’s not clear how to best proceed when the user manages their transform while Avian is also doing that
And Avian can't really know whether a change to Transform came from Avian syncing Position + Rotation or from the user
That would be random success, as that is not the right schedule 😉
The schedule is RunFixedMainLoop
Oh wait, Reset
Sorry, you’re right of course, that one is in FixedFirst
https://github.com/Jondolf/avian/pull/758
gave it a try
Left a tiny review 🙂
@vestal minnow PR looks good now imo!
(And would allow me to delete some code from avian_pickup now, hehe)
Mm I had a random instance where all the static colliders spawned at the center in the dynamic_character_3d example, but I've now rerun the example like 30 times and have not reproduced it 
now I'm just wondering if this was some weird cache thing or if there's an actual bug
tip: I find it often helps to run WebGL2 builds to force ambiguities to show up, as there is no parallelism anywhere.
Just disable multi threading then 
fair enough 😄
running avian's tests locally is fun
I have like a 50% success rate on whether it runs compiles properly
tried a few different command configurations too, including the one CI runs 🥲
also I swear my compile times are like twice as long after I tried to get the Dioxus subsecond stuff working a couple of weeks ago 🤔 and I couldn't even get that working without errors in the end lol
it is
whats 'll that 50% success rate stuff then
🤠 
that's the success rate for whether it finishes compiling everything without failing on errors about resource usage or lld stuff or some other arcane error
if it actually gets to the testing part, it's fine
ah
so this is just an issue on my side but idk how to fix it :P
and now it works fine again, whyyy
STATUS_STACK_BUFFER_OVERRUN is a wild error
have you considered running WSL2?
builds should be more stable on some unix platform
and with newest WSL2 you can run window applications just fine
trying to sort out if i have a bug. is it expected that adding collision components (body, collider) to an entity would cause the entity’s translation to be adjusted by its parent’s scale?
iirc parent translation scale will affect child offset, but this shouldn't be changed by body/collider addition
yeah like it’s actually changing the Transform of the child
static child, non-physics parent
can you drop before/after screenshots?
i can when i get back to my desk, but it’s just gonna be an object in two different positions 😅
i FIXED it by preemptively inversely scaling the child’s translation based on the parent scale, so i know, mathematically, that’s what’s going on. just bewildering that adding the physics components is what’s triggering it
maybe i’ll just try to repro in a test
good idea
i cant repro it in a test, but disabling physics plugins stops it. guess ill revisit tonight. feel a little crazy
maybe it's the sync plugin
Do you have a ~/.cargo/config.toml? If so, try deleting it again
There's a weird bug that is haunting me where children![] sometimes hecks up the hierarchy. In case that's happening to you, using with_children works.
Don't think that's the case here, but mentioning it just in case 🙂
bleh. it's some kind of system ordering issue. instead of having the rigidbody assigned from an observer, using a system ordered before the physics set seems to fix it consistently
spent like an hour trying to repro in a test and couldn't though, sad
oh, I'm getting the linker errors now alright
there is no good way of loading a collider from file is there?
A component that will automatically generate a Collider at runtime using Collider::try_from_constructor. Enabling the collider-from-mesh feature activates support for computing the shape dynamically from the mesh attached to the same entity.
question: what could be causing my player to glitch out like this at very specific angles?
what I'm doing is getting the average point of all the first hits the setting the entity position to the average position
not exactly, I'd have to write my own abstraction :/
seems like you're pushing it into the ground and then the ground ejects you
offset Y for the average
Some people have done this #1278020494687338629 message
I see, yeah seems like i need a custom solution then
good excuse to finally play around with asset loaders, already did procedural textures
@vestal minnow what algorithm/collider making strategy do you suggest for a grid-based physics object?
something that's made out of bunch of uniform squares connected together
Parry's new Voxels shape should be good for this, but we don't support it just yet since we're not on the newest Parry version
kinda related, is there an active plan to make Collider Reflect? or probably not anytime soon
We can probably make it partially Reflect but the internal value is a SharedShape from Parry, which is not Reflect, so I'm not sure how well it'd work
I can make a quick PR to add support for this probably
Alternatively you could make a Collider::compound with a bunch of squares/cuboids, but it most likely wouldn't be as fast, and it'd suffer from ghost collisions against internal edges (i.e. you bump into the "seams"), unlike with a dedicated voxel grid shape
I didn't know Avian used parry
it's the only good and "fully-featured" collision detection library Rust has :P I have my own lib under development but that's very much a long-term thing, still highly WIP
Last time I asked Seb, he said he didn’t want to upstream a bevy_reflect feature and that this is a job for the crates that integrate parry with Bevy 😬
We could remote reflect it
It’s just very much boilerplate
job for the crates that integrate parry with Bevy
...how
wouldn't you need to create matching types for every Parry shape or something
I don’t think Seb quite understood what bevy_reflect is 🙃
I tried telling him that it is a generic reflection thing not inherently tied to Bevy, but he wasn’t convinced
Exactly
Soooo instead of doing that I just reworked AsyncCollider into ColliderConstructor(Hierarchy) 😄
That’s the origin story of that lol
Now that I need to send colliders through BRP for the navmesh editor I guess I'll just serialize and deserialize them with serde
Yeah I'm annoyed because they're making some really really good stuff like Parry (and Rapier), but there are just a million integration problems like this that make me feel like it's just better to have something that is designed to actually work with Bevy (hence developing Avian and Peck and other related crates)
To be fair, that was some time ago, maybe if we asked again he could reconsider
the ship of beveus
But yeah speaking from experience, I really really really think parry is not the right thing to use long term
slowly replacing all dependencies with custom Bevy crates 🙃
Yeah I mean more generally, Parry using Nalgebra is the other obvious big one, and I don't see that ever changing
As much as I am deeply grateful for everything Seb has done for physics in Rust, his APIs just never really clicked for me, nor do the documentations. And converting all geometric data around different libraries is just silly
(But also what valve does for physics lol)
And lastly, beyond the API and integration issues, the parry dependency stack is a huge part of my total compilation times that I have no way to optimize as all.
Speaking about that: peck when? :p
We even have a beautiful FPS game to beta test it with 😉
Yeah, it's mainly just unfortunate since I think both the Dimforge people and us are doing some great stuff, but I don't see a nice way for the projects to really share work or co-operate in a meaningful way, since there are just so many fundamental differences in both the implementation and goals
but yeah I have a lot of thoughts there but won't go on that tangent rn 😅
some day after I'm done with this AVBD rabbit hole I've found myself in
Holy shit is that impressive
wow
And I know the perfect name for your impl
AViaBDn, so simple and easy to pronounce
I want to implement it soooo baaad but I need to somehow decipher how the math and constraints work with this formulation
Another open question is that I'm pretty sure you can't easily do wide SIMD with AVBD, since you basically move each body one by one while keeping the others fixed. But you do get better parallelism with the graph coloring since you assign bodies to colors instead of individual constraints, so you end up with fewer colors
They might send you a snippet if you email them
The demo was on a GPU, right?
So it's potentially really good for a GPU implementation but I'm less sure about a CPU implementation
Yes
If I try implementing this, I'll probably just try a "simple" 2D contact solver first, they at least described contact solving a bit in the paper
Ngl Seb kinda cooked with the voxel stuff, it can even generate voxelized colliders from arbitrary triangle meshes
(obviously you wouldn't actually do this with an environment mesh like this lol)
I'm not sure why it did that to the ground but it's definitely not meant for meshes like that anyway :P
PR coming in a sec
wow that's impressive!
@cerulean zephyr Voxel colliders
https://github.com/Jondolf/avian/pull/761
I don't have a fancy example for this yet, but you can look at the Parry PR for cool images :P
I added a few more APIs that bevy_rapier doesn't have yet, notably creating voxel colliders directly from Bevy Meshes, even with ColliderConstructor
actually you would for navmeshes 👀
(in bevy_rapier you'd need to manually extract vertex and index buffers for this)
What are the limitations on the voxels? Do they have to be uniform sized?
Or can they be higher than they're wide?
- For a given shape, every voxel is the same size, but the x, y, z lengths can be different
- Caveat: It seems like the mesh-to-voxel thing requires uniform sizes :/ so you can only have non-uniform voxel sizes if you manually input the voxel grid
- I believe collisions against composite shapes (trimeshes, convex decompositions) don't work yet, but collisions against other voxels or primitive shapes do work
- There are some crashes in debug mode because of this issue, but not in release mode
Also, caveat: The actual voxelization code is a part of Parry's VHACD port, which is under a different license from the rest of Parry 😕
https://github.com/dimforge/parry/blob/master/src/transformation/voxelization/voxel_set.rs
but I believe it's BSD 3-Clause which should be fine
I don't need any collisions, just the voxelization as part of the algorithm. But I do need a separate Y length :/
I find it kinda annoying that the VHACD and voxelization stuff is a part of Parry
it'd be nice if it was just extracted into its own crate
I'm pretty sure it only uses some math types from Nalgebra and a few AABB methods
Once I do convex decomp in Peck, I'll probably just port Parry's VHACD impl to Glam (or just use no math lib if there's not much math) and make it its own crate
Merged voxel colliders and also this PR to rework Position and Rotation to be required components
https://github.com/Jondolf/avian/pull/760
can offset either one with different methods
well, the sprite is perfectly centered and the collider is on one of the edges
I could change sprite anchor, but then my transform is in an odd spot
or i could split it into a transform hierarchy, but then calculating the offset is annoying because both the sprite and collider are asset-baset
i remember there being a better solution, but you can make it a compound collider with one element
for specifically parry voxels, it looks like you can just offset the voxels on creation?
compound collider sounds like a neat hack, i'll probably use that
i know i can offset by full voxels, but if if my shape has odd dimensions i can't offset by half voxel
unless there is some other offset i missed
you can also have a child with the collider, the offset then being the local Transform. i do the compound collider thing tho
I Know this specific topic might have been and gone by now but i looked at what you posted and, what???? physics seemingly, purely on the GPU seems so foreign, like how??? especially with a bvh like is it built and maintained with compute shaders or something?? I'll have to read up on it more thoroughly
Physics on your GPU? More likely that you'd think
That said there are a lot of GPU algos for things. The main issue is that GPU-accelerated work is naturally less portable and flexible
BVHs are used for ray tracing so that's not uncommon, but yeah GPU physics for game purposes is kinda rare. Research papers use GPU physics all the time though
The theoretical bandwith you get with a GPU is wayyy higher, but physics engines tend to have lots of bookkeeping and random access that GPUs just aren't that good for. So you often need to make some algorithmic sacrifices to make GPU physics work.
And perhaps more importantly, games tend to want physics code to be local to the CPU for game logic purposes. You want callbacks that modify simulation state, you want the simulation state to be directly accessible... you can probably make things like this work with the GPU too to some extent, but it's harder and likely has more overhead
Bepu's article on this is pretty good
https://www.bepuentertainment.com/blog/2019/1/16/-but-gpus-arent-always-the-right-choice
In the last post , the fictional Toilet Toilers Interactive found themselves in a bit of a pickle with their GPU physics-and-everything-else pipeline. They might be regretting some of their technical choices, but it’s worth looking back at some of the things they might have considered during develop
That being said, I do want to experiment with GPU physics at some point (at least for the solver) even if it doesn't end up in Avian
I haven't really done much GPU stuff or worked with compute shaders before, so I want to learn how it all works in practice
Oh hey I need a SymmetricMat6 type for AVBD, which I was also going to need for a better impulse-based fixed joint implementation, so I guess I'm not totally wasting time experimenting with AVBD 😂
The compound collider trick failed, with voxel colliders as children it just phased through everything :/
I'm slowly drifting to Transform offsets, I need to figure out how picking works, whether it's the rigid body entity or collider
okay, so this is the collider from Collider::voxels, one of the corners straight up doesn't corner 🤣
I think this would behave better if it were separate convex colliders
i guess i can try doing voxel -> polyline -> voxel convex decomp?
so... it created a bunch of tiny colliders on the edges 🫠
Jondolf, lmk if it's bothering you when i talk about my experiments here
Does anyone have an idea why the avian ci for docs work 'as-is' but mine gets an error (https://github.com/cBournhonesque/lightyear/actions/runs/15762452174/job/44432055277)
The system library `wayland-client` required by crate `wayland-sys` was not found.
I'm using an identical configuration: https://github.com/Jondolf/avian/blob/main/.github/workflows/ci.yml#L29
there is no wayland-client mentioned in the docs job you sent
you're right, my browser failed to find it
there is here: https://github.com/cBournhonesque/lightyear/actions/runs/15762452174/job/44432055277#step:5:831
is it because some of my crates enable some rendering related features, but avian doesn't?
hm let me try that, thanks!
Can't you just compound collider the boxes instead?
.
i've tried it, it wasn't bad
Alternatively you could try to combine them together like with greedy meshing so there are fewer internal edges and more performance
yeah, that's what im doing right now
greedy meshing should be plenty for my needs
it seems like something changed in 0.16, I am no longer seeing changes to transform backed into position
if I change the position in the editor, position never updates. If the entity is sleeping, the entity transform remains the same with no effect from gravity
Is this in Avian 0.3 or the main branch?
Does that only happen if the body is sleeping when you change the transform?
hard for me to tell, I'll test with sleep disabled
with sleeping disabled I can't even change the position in the editor
though I suppose that may be a system ordering issue
That seems very strange, I have many examples that just manually change transforms or Position/Rotation and they work fine
Even if I pause physics, and modify the transform directly, it still updates the physics position correctly 🤔
@vestal minnow I'm trying to clone physics entities, it seems transform cloning and modifying isn't enough because the entities reset back to 0, 0
is this an ordering issue on my part or do i need to clone+update Position and Rotation?
because i want to apply offset when cloning
nvm, seems to be a cloning issue, my transforms aren't actually updated
or... something from the physics backend could be setting my GlobalTransform to 0
it has to be physics backend, removing RigidBody fixes the position
Oh yay I can successfully send colliders over BRP 😄
Right window is the "game", left window is the "editor"
obviously not must editing happening yet
But it seems to successfully send the mesh + colliders over!
Just takes like 2 seconds for this fairly simple scene due to (de)serialization 
Yeah a while ago i made a bvh based ray marcher, but the bvh was built and maintained cpu side and used gpu side, perhaps i read it wrong but i thought the paper said that it was all gpu based which seems foreign to me. i would love to have a shot at implementing something like what the paper shows but physics stuff is kinda out of my league at the moment, so i would need to make some physics related projects before i even attempt that, not even including wrapping my head around how to actually get a compute shader going, which i couldn't figure out last time, although that was back around 0.11 or maybe 0.12 days.