#Avian Physics

1 messages · Page 35 of 1

visual sparrow
#

Btw @vestal minnow Foxtrot is on Avian main now and it’s working great!
Especially the collider caching perf gains are absolutely lovely to have 🙂

vestal minnow
#

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

cinder summit
#

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 🤣

little maple
#

I was surprised a sphere would.. but I guess that's just a distance check from a point... that makes sense

#

yeah

vestal minnow
#

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.

weary mortar
#

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 😁

viscid crown
#

sorry for the side-topic, is this just iyes_perf_ui for the visual portrayal? or something local/bespoke?

vestal minnow
#

added in 0.3

viscid crown
#

thanks!

vestal minnow
#

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:

  1. Full rebuild: Just build the whole BVH from scratch every time with PlocSearchDistance::Minimum. Other configs seem to be way more expensive.
  2. Remove and insert: Use remove_primitive and insert_primitive for each moved proxy/primitive.
  3. Resize and reinsert: Use resize_node to enlarge each moved proxy and refit their ancestors, and reinsert_node to find a better spot in the BVH.
  4. Resize and optimize: Use resize_node (or I also made a simpler custom version) for each moved proxy, and then later run the ReinsertionOptimizer on the whole BVH. The reinsertion can ideally be done in parallel with the narrow phase, partially hiding the cost. Right now I'm using a batch_size_ratio of 0.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:

  1. Full rebuild: 4.5 ms
  2. Remove and insert: 6.25 ms
  3. Resize and reinsert: 4.9 ms
  4. 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:

  1. Shape proxies are incrementally added to / removed from the BVH when the shapes are spawned/despawned or teleported. The insertion_removal branch for OBVHS makes this possible :D
  2. At the end of the time step (in b2Solve), serially enlarge broad phase proxies (with b2DynamicTree_EnlargeProxy). This updates the AABB of the enlarged proxy's node, and refits ancestors, marking them as moved nodes using a simple u16 bitflag 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.
  3. During the next time step, the broad phase runs like normal, and finds intersecting pairs of AABBs.
  4. 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:
    1. 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.
    2. 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 :)

sleek thicket
#

this is art.

vestal minnow
#

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)
cinder summit
#

f64 AABBs (for large worlds)
I wonder if there isn't a better approach here

#

Going for f64s would be a big slowdown 🤔

vestal minnow
#

you could have multiple BVHs around different origins

#

and make queries relative to that

#

which is vaguely related to big_space support too

sleek thicket
#

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

vestal minnow
#

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

vestal minnow
#

and for shape casts it uses a base offset

thorn solstice
#

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?

thorn solstice
#

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)

vestal minnow
#

same as your physics example essentially

#

I'll try run_with_candidates too

thorn solstice
vestal minnow
#

(ignore the overly bright screenshot, discord does that for some reason when I take screenshots)

#

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

thorn solstice
# vestal minnow My wishlist for hypothetical future physics-oriented stuff in OBVHS would probab...

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.

thorn solstice
vestal minnow
thorn solstice
#

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..

vestal minnow
#

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

cinder summit
#

Feature flag doesn't sound ideal here tbh 🤔

vestal minnow
thorn solstice
#

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

cinder summit
#

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

cinder summit
thorn solstice
#

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..

cinder summit
#

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

thorn solstice
#

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

cinder summit
#

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 ferris_spooky

vestal minnow
#

clearly we need a BVH with k-DOPs

thorn solstice
#

BoundingSignedDistanceFieldHierarchy

#

BoundingNonUniformRationalBSplineHierarchy

cinder summit
#

Time to accelerate our hierarchy using a BVH!

cinder summit
vestal minnow
#

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

thorn solstice
vestal minnow
#

they're very cheap to store and the intersection test is similar to AABB-AABB intersection tests

thorn solstice
#

I guess collision detection can use them directly though so..

vestal minnow
#

but yeah idk what/who actually uses them

thorn solstice
#

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)

cinder summit
#

Honestly I've been considering adding a step between raymarching and the AABB to further reject volumes

thorn solstice
cinder summit
#

Yea, same could be true for physics when the collider is an expensive type. But plenty of colliders can be trivially cheap to check

thorn solstice
#

Could have something like a baked quantized SDF matrix. Could be maybe ~8 bits per cell, and then basically fit 2x2x2 into a u64

vestal minnow
#

there are actually some papers about using deep learning for building trees lol

cinder summit
#

There is a type of SDFs involving AI ferris_spooky

vestal minnow
#

of course there is

cinder summit
#

Neural Implicit Surfaces iirc

vestal minnow
#

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?

cinder summit
vestal minnow
#

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

silk mauve
#

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?

sweet sundial
#

add Position

random juniper
#

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?

knotty thicket
#

I tend to set them globally in these kinds of use cases:

.insert_resource(DefaultFriction(Friction::new(0.)))
.insert_resource(DefaultRestitution(
    Restitution::new(1.),
))
random juniper
#

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

elder narwhal
#

try locking the rotation of all involved bodies as chris mentioned

random juniper
#

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.

random juniper
#

yup just found it sorry. so the paddles have LockedAxes::ALL_LOCKED.unlock_translation_y(), and the ball has LockedAxes::ROTATION_LOCKED. not working...

knotty thicket
#

"not working" doesn't describe a problem we can help with

random juniper
#

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

knotty thicket
#

dynamic rigid bodies are affected by forces, including the force of the ball on the paddle

random juniper
#

also let me update the gh repo if you wouldn't mind taking a look

knotty thicket
#

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

random juniper
#

appreciate it

vestal minnow
#

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

weary ember
#

How do I manually set moment of inertia for a collider?

vestal minnow
#

(note that in 2D it has a simpler representation and API, so look at the docs for that if you're using 2D)

weary ember
#

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);

vestal minnow
#

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 🤔

vestal minnow
#

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

#

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

vestal minnow
#

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

cinder summit
#

Yea the thread scaling looks a bit sad 🥲

vestal minnow
#

For reference, Box2D looks like this

cinder summit
#

Now that looks more like actual scaling

vestal minnow
cinder summit
#

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

vestal minnow
#

iirc single-threaded Box2D v3 is still like twice as fast as v2 (which didn't have any multi-threading)

#

but yeah

vestal minnow
#

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

visual sparrow
vestal minnow
#

mmm I really want to see bevy_rapier on this plot too

vestal minnow
#

eh, tomorrow :P

vestal minnow
#

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

visual sparrow
vestal minnow
#

Yeah so with a more realistic benchmark of a bunch of separate smaller pyramids, Rapier scales slightly more and is faster overall

visual sparrow
visual sparrow
#

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?

vestal minnow
vestal minnow
visual sparrow
vestal minnow
#

Once we're reliably faster than Rapier in both 2D and 3D 😤

visual sparrow
vestal minnow
#

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

edgy light
#

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

visual sparrow
vestal minnow
#

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

vestal minnow
#

So yeah... that's the kind of perf I'm ideally aiming for, it kinda leaves Rapier in the dust :P

visual sparrow
visual sparrow
vestal minnow
#

idk if there's something going horribly wrong there though, I'd need to check the internal timers for a detailed breakdown

vestal minnow
#

that Box2D scene has 58,000 contact pairs

#

and 22,001 bodies

vestal minnow
#

graph coloring and wide SIMD are probably doing some heavy lifting there, though it still doesn't explain that drastic of a difference

rocky seal
#

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.

rocky seal
#

is the velocity gain from an impulse just the vector length divided by the mass?

sweet sundial
#

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())

sweet sundial
vestal minnow
#

The former (which Avian uses) is the Padé approximation of exponential decay, while the latter is the actual formula for exponential decay

vestal minnow
#

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

rocky seal
#

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

willow elbow
#

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!

little maple
willow elbow
clear dew
#

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

pliant mountain
#

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

nimble fiber
#

I was just about to ask this question lol^

I ended up using spatial queries instead to do a shape intersection check

pliant mountain
#

yeah thats what i thought as well

vestal minnow
pliant mountain
#

im realizing now i could also just do this with a joint

vestal minnow
#

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

pliant mountain
#

aah that was my mistake lol my bad

#

that makes sense

vestal minnow
#

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

pliant mountain
#

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

nimble fiber
#

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.

halcyon dew
#

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

weary ember
#

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?

glad eagle
#

Is there an easy way to pause physics systems?

spare lily
glad eagle
little maple
quartz heart
#

should the PhysicsPickingPlugin bubble events up the hierarchy if a child collider is "picked"?

halcyon dew
little maple
halcyon dew
vestal minnow
#

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

vestal minnow
#

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)

vestal minnow
# halcyon dew hello friends, anyone have tips for figuring out why my chain is just kinda goin...

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_factor and contact_damping_ratio in the SolverConfig
  • Make the chain particle colliders bigger
  • Make sure you have an appropriate PhysicsLengthUnit (can be important if you're using pixels as units)
halcyon dew
#

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!

vestal minnow
#

It could definitely make sense to bubble to the rigid body through ColliderOf

quartz heart
#

I do think it would make sense fo the events to bubble up to the top rigidbody from child colliders.

vestal minnow
#

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 ferris_sob

willow elbow
visual sparrow
willow elbow
willow elbow
#

(I just disabled PhysicsDebugPlugin and the colliders are still spawned there with picking behavior)

visual sparrow
willow elbow
# visual sparrow 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),
));
dusky brook
#

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

visual sparrow
#

Shouldn't you set the velocity on them directly?

dusky brook
#

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

visual sparrow
# willow elbow it's really nothing special: ``` let _id = commands.spawn(( Name::new(format...

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

dusky brook
#

hmm this is 2d physics, should i still try that?

visual sparrow
dusky brook
#

i saw mesh in the diff :p

#

can confirm that ExternalForce has no effect on the rigid body when it's kinematic 👍

visual sparrow
dusky brook
#

ohh ok

visual sparrow
visual sparrow
halcyon dew
#

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

clear dew
clear dew
#

ok I'm gonna try to reproduce

dusky brook
#

lmk if i can provide more details for reproduction

clear dew
dusky brook
#

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);
        }),
    )
clear dew
#

you know what's strange

#

so for a second I was like, yeah, nothing's happening, then I added your capsule and it worked

dusky brook
clear dew
#
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

dusky brook
#

i have zero gravity as well

clear dew
#

ahhh. wait

quartz heart
# vestal minnow I think observers are missing the `currentTarget` equivalent that the web has

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).

clear dew
dusky brook
#

as another data point, switching to non-persistent force applied every frame fixes the issue

clear dew
dusky brook
vestal minnow
clear dew
dusky brook
clear dew
#

haha ok

clear dew
#

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

reef owl
#

@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.

sleek thicket
nimble fiber
knotty thicket
#

did I miss a "heightfield-from-mesh" function somewhere?

visual sparrow
vestal minnow
#

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

vestal minnow
# clear dew does anyone have some point-of-contact code I could yoink for `Trigger<Collision...
.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,
        );
    }
});
clear dew
#

dude that was so crazy, I literally just checked this channel

#

thanks!! hahaha

#

like just opened it immediately before you sent that

vestal minnow
#

lmao

clear dew
vestal minnow
#

you'd just check which one is contact_pair.entity1 and swap the points if necessary

clear dew
#

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

vestal minnow
#

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)

clear dew
#

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

vestal minnow
#

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);
    }
}
nimble fiber
# vestal minnow a basic setup that should work is like ```rust commands.spawn(( RigidBody::D...

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?

vestal minnow
#

They should trigger sensors

#

What are the layers for the static body?

nimble fiber
#

Static body: memberships 33, filters 0
Sensor: memberships 2, filters 32

vestal minnow
#

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

nimble fiber
#

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

vestal minnow
#

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
nimble fiber
#

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

vestal minnow
#

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

nimble fiber
#

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

vestal minnow
#

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

nimble fiber
#

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.

dense current
#

Hi i'm new to avian3d and want to learn about how can i make ragdoll physics, is there anyone worked on this ?

clear dew
#

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.

vestal minnow
#

Haven't done too much CLI stuff with Rust so this was fun

#

clap is so nice

#

okay Rust workspace rules are dumb ferris_sob 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

vestal minnow
#

kk it seems to work now, back to jamming bevyjam

runic tinsel
#

swallows

#

"Hey have you been working on anything?"
"Oh yeah, 2 sec, I have a small change..."
Rewrites codebase

vestal minnow
#

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

cinder summit
#

It' s not even done and we're doing 91 files, +3k, -14k

vestal minnow
#

what's that -14k from 👀

cinder summit
#

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

spare lily
#

Anyone has miltiple fixed joints code example? 2D

sweet sundial
#

spawn multiple entities, one for each joint

spare lily
#

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

spare lily
#

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...

clear dew
#

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

clear dew
#

I guess that's probably the move

knotty thicket
#

I'm a fan of simple-and-slightly-duplicated over complex

spare lily
#

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

sweet sundial
#

might be a scale issue, how big are they and what's the physics unit

nimble fiber
#

^ 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.

spare lily
plush pike
#

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)

vestal minnow
#

TLDR: possible yes, with a custom SyncPlugin, but no easy built-in support atm

plush pike
#

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

vestal minnow
#

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

plush pike
#

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

spare lily
#

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

shrewd warren
#

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.

vestal minnow
#

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?

vestal minnow
#

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

shrewd warren
#

The 2 cuboids keep sliding and the ball keeps rolling

#

the floor is static with friction 1.0

#

the rest has friction 0.3

vestal minnow
#

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

shrewd warren
#

My units are in meters, as bevy expects?

#

And thanks for the quick response!

#

Anybody here making a pinball game using avian?

shrewd warren
#

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?

vestal minnow
# shrewd warren My units are in meters, as bevy expects?

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

shrewd warren
#

I'm trying to model a pinball table of 0.5 m * 1.0 m with the ball diameter being 0.027 m

vestal minnow
vestal minnow
#

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

halcyon dew
#

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?

shrewd warren
dreamy raft
#

is it possible to limit the length of a raycast?

vestal minnow
#

There should be a max_distance argument on the methods

dreamy raft
#

cool thank you

vestal minnow
#

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 Name for 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.
halcyon dew
vestal minnow
#

Or if you make it a child and despawn the parent, it will be despawned yeah

halcyon dew
#

that feels intuitive enough, thank you for your detailed responses :)

shrewd warren
sweet sundial
#

is it a fixed joint or a set of child colliders

shrewd warren
#

Childs to the center box

muted goblet
#

Hi, got some silly questions. is it possible to get the area of a collider, and what unit is ExternalImpulse, is it in newton?

sweet sundial
#

it's in Δv/m, technically the units are unspecified

muted goblet
#

uh, do you know how do i convert newtons into that? idk what that is

sweet sundial
#

if your length and mass units are meter and kg, i think it's in Ns? or N/s?

muted goblet
#

yes it is, many thanks!

sweet sundial
fringe mango
#

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?

dusky brook
#

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

heavy thistle
#

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?

dusky brook
sweet sundial
visual sparrow
#

Got some weird instances of the NPC supposedly hitting itself happy

dusky brook
#

feature

past escarp
#

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

fringe mango
#

is there a way to prevent an entity's collider from interacting with other specific entities rather than using an entire collision layer?

sweet sundial
fringe mango
#

thanks, i'll check that out

#

when i'm checking the pairs, do i have to check "both ways" in a collision pair?

heavy thistle
#

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"?

sweet sundial
#

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

heavy thistle
#

wait i think i said that wrong

#

i have two platforms that are next to each other but they are static otherwise

sweet sundial
#

that doesn't look very static

heavy thistle
#

lol it happens if it's static too

sweet sundial
#

in 2d you might be able to circumvent it with 2d capsules

heavy thistle
#

will try

heavy thistle
#

this did not work

#

oh wait

#

it did work, i just had to stop them from overlapping

#

that's strange, but thanks

halcyon dew
#

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

sweet sundial
#

store whether you've reacted in a local or component

dire mountain
#

is it possible to define an offset for a Collider position ?

vestal minnow
vestal minnow
#

but main has better performance 😉

vestal minnow
#

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

dire mountain
#

I see, muchas gracias !
I'm inserting the Collider as a required component , so i'll try the Collider::compound solution 👍

clear dew
#

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

little maple
clear dew
clear dew
#

so ill see above fixing that

#

@visual sparrow it is faster...I think it might just be the model 😄

visual sparrow
#

would it be bad for optics if you just made them all sphere colliders?

clear dew
#

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

visual sparrow
#

Or did you run into a bug?

#

Oooh probably the thingy you mentioned before

clear dew
#

Several yeah

visual sparrow
#

Well I'll be interested in using your fork then 😄

clear dew
#

Spheres are the simplest of the 3d colliders right? Over cuboids? Not sure how aabb3d would work. Intuitively, seems spheres would be simplest

quartz heart
#

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?

visual sparrow
vestal minnow
#

(or use main)

rocky seal
#

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

halcyon dew
# rocky seal i'm running my sim headless - no rendering. is there a way for me to run the phy...

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);
    }
}
clear dew
#

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"

visual sparrow
clear dew
#

no

#

gotta run tracy with windows aagh

#

tomorrow me's problem

shrewd pulsar
#

kind of annoying and janky but it works on linux

visual sparrow
#

Check the profiling guide

clear dew
#

gah alright

visual sparrow
#

It’s much simpler than getting tracy to do what you want 🙂

clear dew
#

the profiling guide's all about tracy and lowkey I kinda wanna see what that thing can do 😆

#

but ur right

visual sparrow
#

If you need to explore what even is the bottleneck, a flamegraph is the better tool imo

runic tinsel
#

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?

clear dew
#

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

runic tinsel
#

It is indeed called…

#

I mean that is useful to know I guess…

visual sparrow
#

You can click the parts then

clear dew
#

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

nimble fiber
#

^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.

rocky seal
#

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

halcyon dew
#

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

rocky seal
#

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

halcyon dew
#

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

rocky seal
#

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

halcyon dew
#

if all you need is fixedupdate systems to run between each physics step then that sounds reasonable enough to me

rocky seal
#

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

halcyon dew
#

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

rocky seal
#

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

halcyon dew
#

🎉

rocky seal
halcyon dew
#

sure thing!

rocky seal
reef owl
#

@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?

vestal minnow
#

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

reef owl
dreamy raft
#

does avian provide any built in gizmo implementation for viewing collision or will I have to do that myself?

halcyon dew
#

also i managed to run into performance issues in a game with like ~100 colliders. no broad phase is rough

nimble fiber
#

^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.

dreamy raft
#

also some godot plugins are C#, or GDScript, or C++ or rust or whatever the developer decided to use

nimble fiber
# dreamy raft also some godot plugins are C#, or GDScript, or C++ or rust or whatever the deve...

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.

viscid crown
#

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?

bold garnet
nimble fiber
#

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?

glad eagle
#

can i make a sphere that attracts objects? like make it have gravity of its own

viscid crown
daring ember
#

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.

sweet sundial
#

i'm pretty sure CollisionStarted is after the first tick of a collision

muted goblet
#

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?

sweet sundial
#

sounds like you need some kind of damping

muted goblet
#

oh

#

i havent implemented drag

sweet sundial
daring ember
fringe mango
#

is there any way to make a collider from a bevy primitive?

#

kinda sucks having to rewrite code like this :(

vestal minnow
#

or alternatively my_primitive.collider()

#

it's not supported for all of Bevy's shapes atm, but it should work for the common ones

halcyon dew
fringe mango
#

awesome thank you guys

sweet sundial
#

i've got a utility fn collider_mesh<T: IntoCollider + Into<Mesh> + Clone>(t:T, &mut Assets<Mesh>) -> (Collider, Mesh3d)

past escarp
#

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),
halcyon dew
#

your compliance seems quite high, have you tried reducing it

daring ember
#

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?

sweet sundial
#

what data do you have and what sort of result do you want?

daring ember
#

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

sweet sundial
#

do you expect it to ever be concave?

daring ember
#

Yes

daring ember
#

Fantastic, thank you!! I tried going through all docs but couldn't spot that

sweet sundial
#

oh, though collider seems like it'd treat it the same as a polyline anyway so it might be irrelevant

#

probably worth testing

cinder summit
#

@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 🤔

cinder summit
#

Also getting a bunch of warnings and panics when diagnostics aren't enabled, which doesn't like particularly desirable behavior 🤣

cinder summit
#

You mean the debug plugin? 🤔

glad eagle
#

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

quick saffron
cinder summit
quick saffron
#

Figured it out:

cinder summit
#

If both in FixedX there should be no difference if either or both aren't in the Fixed schedules it's not surprising

quick saffron
#

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

heavy thistle
#

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?

quartz heart
#

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.

dreamy raft
#

is there any way to query or check for a specific collider?

dreamy raft
#

I want to utilize the diameter and half height of a capsule collider but that's just not something everything has

sweet sundial
#

fn tocap(c:&Collider) -> Option<&Capsule3d> { c.shape().downcast_ref::<Capsule3d>() }

dreamy raft
#

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;
        }
    }
}
little maple
dreamy raft
#

I'm using the raycaster and just trying to get the position of the first, non player, that it hits

past escarp
#

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

past escarp
scenic violet
#

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

#

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!

merry egret
#

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

merry egret
#

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

merry egret
#

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

eager meadow
#

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"?

sweet sundial
#

seems generally like being outside of colliders is the expected situation, but trimeshes also don't seem to have differentiable sides

rocky seal
# rocky seal advancing the clock doesn't seem to work but setting the relative speed of `Time...

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?

eager meadow
#

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.

eager meadow
#

Oddly its also doing so if I use a Polyline...

viral goblet
#

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

eager meadow
#

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?

sweet sundial
#

i put most of my systems .after(PhysicsStepSet::Last)

sweet sundial
#

Vec3::ZERO + 6.0 gives (6,6,6)

eager meadow
#

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)

sweet sundial
#

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.

eager meadow
#

....

#

Goddammit

#

Thank you

sweet sundial
#

o7

hexed veldt
#

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>,
vestal minnow
#

and use collisions.iter()

#

(or collisions.get(entity1, entity2) if you want to get data for a specific contact pair)

hexed veldt
#

Is during_previous_frame gone?

#

I was using it in 0.2

vestal minnow
#

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

hexed veldt
#

I was trying to only process a pair at most once

dreamy raft
#

how do I get the position of a hit from a raycast?

halcyon dew
#

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

dreamy raft
#

is there any existing means to transform it or will I have to do the matrix math?

sweet sundial
#

divide by GlobalTransform

hexed veldt
# vestal minnow If you just want to know if the collision started/ended, there are `collision_st...

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
stray mantle
#

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.)

visual sparrow
stray mantle
#

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.

marble flicker
#

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‎ ?

marble flicker
#

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);
visual sparrow
#

What’s the reason you’re combining the colliders into a compound collider? There’s AFAIK no performance gained by doing so

visual sparrow
marble flicker
#

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

visual sparrow
#

If not, you can check the cache impl if you want to replicate it. It’s basically a HashMap<Handle<Mesh>, Collider> 🙂

marble flicker
#

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

hexed veldt
#

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.

sweet sundial
#

where's it scheduled?

vestal minnow
#

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

hexed veldt
#

The collide system is scheduled in PostUpdate if that is helpful

cerulean zephyr
#

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

cerulean zephyr
#

@vestal minnow

vestal minnow
#

It could also be related to the current joints behaving weirdly with contacts like in #1124043933886976171 message

cerulean zephyr
#

multiples of 50 with default physics unit length

vestal minnow
#

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

cerulean zephyr
#

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?

vestal minnow
#

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

cerulean zephyr
#

good point

#

thanks

vestal minnow
#

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

cerulean zephyr
#

all in all it's good that this came up in a very simple scenario

vestal minnow
# cerulean zephyr yeah, i've had a rough read through all the features, I must have missed the uni...

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...)

cerulean zephyr
#

mhm

shy blaze
#

is there a possibility of a ColliderConstructor::Combined(Vec<ColliderConstructor>) ?

visual sparrow
#

I believe so

#

We would just need to unpack nested compound colliders, as parry panics otherwise

#

But that's fairly simple

visual sparrow
shy blaze
clear dew
#

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

vestal minnow
viscid crown
#

what kind of mad science is cookin haha

vestal minnow
#

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

clear dew
#

it works totally fine actually 😄

#

yeah, that'd be cool someday!

vestal minnow
#

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 RecomputeGlobalTransform sparse-set component that has a lifecycle hook that immediately updates the GlobalTransform with the TransformHelper when the component is inserted
  • Require RecomputeGlobalTransform for RigidBody and Collider, causing their global transforms to be initialized with correct values
  • Require Position and Rotation for RigidBody and Collider with PLACEHOLDER values (to distinguish between user-defined values and default values), but use a lifecycle hook to immediately update them to match the GlobalTransform
#

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

vestal minnow
visual sparrow
#

@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 😛

vestal minnow
# visual sparrow <@545959292281552928> I'm currently experimenting with creating an avian-first n...

Some likely changes that come to mind

  • Make Position and Rotation required 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

visual sparrow
#

And are the BVHs exposed?

#

Ah, and do you mind if I pick the name avian_navmesh?

vestal minnow
# visual sparrow And are the BVHs exposed?

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)

visual sparrow
#

Ah, nice!

vestal minnow
clear dew
#

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

visual sparrow
visual sparrow
#

Nvm, that case should be alright

#

After looking at it for a while, I'm not sure where the NaN could come from hmm

visual sparrow
rocky seal
#

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

visual sparrow
rocky seal
#

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

visual sparrow
#

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

visual sparrow
#

The schedule is RunFixedMainLoop

#

Oh wait, Reset

#

Sorry, you’re right of course, that one is in FixedFirst

visual sparrow
visual sparrow
#

(And would allow me to delete some code from avian_pickup now, hehe)

vestal minnow
#

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 ferris_sob

#

now I'm just wondering if this was some weird cache thing or if there's an actual bug

visual sparrow
cinder summit
#

Just disable multi threading then thonk

visual sparrow
vestal minnow
#

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

cerulean zephyr
#

wha

#

i thought avian was deterministic

vestal minnow
#

it is

cerulean zephyr
#

whats 'll that 50% success rate stuff then footgun 🤠 footgun

vestal minnow
#

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

cerulean zephyr
#

ah

vestal minnow
#

so this is just an issue on my side but idk how to fix it :P

#

and now it works fine again, whyyy

cerulean zephyr
#

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

shy blaze
#

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?

cerulean zephyr
#

iirc parent translation scale will affect child offset, but this shouldn't be changed by body/collider addition

shy blaze
#

yeah like it’s actually changing the Transform of the child

#

static child, non-physics parent

cerulean zephyr
#

can you drop before/after screenshots?

shy blaze
#

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

cerulean zephyr
#

good idea

shy blaze
#

i cant repro it in a test, but disabling physics plugins stops it. guess ill revisit tonight. feel a little crazy

cerulean zephyr
#

maybe it's the sync plugin

visual sparrow
visual sparrow
#

Don't think that's the case here, but mentioning it just in case 🙂

shy blaze
#

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

cerulean zephyr
#

oh, I'm getting the linker errors now alright

cerulean zephyr
#

there is no good way of loading a collider from file is there?

sweet sundial
dreamy raft
#

what I'm doing is getting the average point of all the first hits the setting the entity position to the average position

cerulean zephyr
cerulean zephyr
#

offset Y for the average

vestal minnow
cerulean zephyr
#

I see, yeah seems like i need a custom solution then

#

good excuse to finally play around with asset loaders, already did procedural textures

cerulean zephyr
#

@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

vestal minnow
#

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

shy blaze
vestal minnow
#

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

vestal minnow
#

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

cerulean zephyr
#

I didn't know Avian used parry

vestal minnow
#

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

visual sparrow
#

We could remote reflect it

#

It’s just very much boilerplate

vestal minnow
#

wouldn't you need to create matching types for every Parry shape or something

visual sparrow
visual sparrow
#

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

vestal minnow
visual sparrow
cerulean zephyr
#

the ship of beveus

visual sparrow
cerulean zephyr
#

slowly replacing all dependencies with custom Bevy crates 🙃

vestal minnow
visual sparrow
#

(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.

visual sparrow
visual sparrow
vestal minnow
#

but yeah I have a lot of thoughts there but won't go on that tangent rn 😅

vestal minnow
visual sparrow
#

wow

#

And I know the perfect name for your impl
AViaBDn, so simple and easy to pronounce

vestal minnow
#

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

visual sparrow
visual sparrow
vestal minnow
#

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

vestal minnow
#

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

edgy light
#

wow that's impressive!

vestal minnow
#

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

visual sparrow
vestal minnow
visual sparrow
#

Or can they be higher than they're wide?

vestal minnow
# visual sparrow What are the limitations on the voxels? Do they have to be uniform sized?
  • 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
#

but I believe it's BSD 3-Clause which should be fine

visual sparrow
vestal minnow
#

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

vestal minnow
cerulean zephyr
#

i don't think this can be concave xD

#

i need to figure out how to center this

sweet sundial
#

can offset either one with different methods

cerulean zephyr
#

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

sweet sundial
#

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?

cerulean zephyr
#

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

shy blaze
#

you can also have a child with the collider, the offset then being the local Transform. i do the compound collider thing tho

dusty bolt
cinder summit
#

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

vestal minnow
#

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

#
#

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

vestal minnow
cerulean zephyr
cerulean zephyr
#

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?

cerulean zephyr
#

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

dreamy viper
#

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

GitHub

A networking library to make multiplayer games for the Bevy game engine - chore(docs): fix tests, cargo doc, cargo clippy · cBournhonesque/lightyear@fe0bb4a

GitHub

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

cerulean zephyr
dreamy viper
#

is it because some of my crates enable some rendering related features, but avian doesn't?

dreamy viper
#

hm let me try that, thanks!

cinder summit
cerulean zephyr
#

i've tried it, it wasn't bad

cinder summit
#

Alternatively you could try to combine them together like with greedy meshing so there are fewer internal edges and more performance

cerulean zephyr
#

yeah, that's what im doing right now

cerulean zephyr
#

greedy meshing should be plenty for my needs

jovial herald
#

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

vestal minnow
jovial herald
#

0.3

#

the orange outline is the collider debug-render gizmo

vestal minnow
#

Does that only happen if the body is sleeping when you change the transform?

jovial herald
#

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

vestal minnow
#

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 🤔

cerulean zephyr
#

@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

cerulean zephyr
#

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

visual sparrow
#

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 bavy

dusty bolt
# vestal minnow BVHs are used for ray tracing so that's not uncommon, but yeah GPU physics for g...

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.