#Avian Physics

1 messages Β· Page 14 of 1

peak schooner
#

Also, a quick thank you for all of your work on the crate. It's been a blast to use so far. I've made some pretty fun little demos w/ it. This is the first thing that's stumped me (so far).

vestal minnow
#

XPBD patent situation update: #1171955566436946031 message

#

I still intend to work on the solver switch and rebrand regardless, since it should be beneficial anyway, and I'd like the name of the crate to not be tied to a specific simulation method

#

But XPBD could still be available as an option, maybe even used just for joints and some other things but not for collisions

patent vigil
#

having the same issue using ldtk and xpbd_2d 4.2, it causes entities with physics to be rendered behind their intended layer

zenith tiger
#

I'm also using ldtk, so it might be related to that - I also noticed that the z changes depending on what layer the entity is on in ldtk, but is only negative when there's a collider

patent vigil
#

yes

#

same

#

usually ldtk has layers increase z by 1 per layer starting from the bottom

#

but with a collider it seems that now my entities local transform.z gets offset by -N where N = layer

#

so the global transform ends up being 0

#

meaning it renders behind everything else

#

also this didnt happen in bevy 0.12 + xpbd 0.3 for me, only showing up now as ive upgraded to 0.13 + 0.4

zenith tiger
#

oh interesting, I only started using this a couple days ago so I didn't realize it worked on earlier versions

silk mauve
#

I'm interested to know how people are approaching collision logic. As far as I can tell, you must evaluate collisions both ways in order to cover all your bases. Here's an example of one of my collision systems:

fn despawn_on_collision_system(
    mut collision_event_reader: EventReader<CollisionStarted>,
    ability_query: Query<&HitMatrix, With<DespawnOnCollision>>,
    team_query: Query<&Team>,
    mut commands: Commands,
) {
    for CollisionStarted(entity_1, entity_2) in collision_event_reader.read() {
        despawn_on_collision(&ability_query, *entity_1, *entity_2, &team_query, &mut commands);
        despawn_on_collision(&ability_query, *entity_2, *entity_1, &team_query, &mut commands);
    }
}

fn despawn_on_collision(ability_query: &Query<&HitMatrix, With<DespawnOnCollision>>, origin_entity: Entity, collided_entity: Entity, team_query: &Query<&Team>, commands: &mut Commands) {
    let Ok(origin_hit_matrix) = ability_query.get(origin_entity) else {
        return;
    };

    let Ok(origin_team) = team_query.get(origin_entity) else {
        return;
    };

    if let Ok(collided_with_team) = team_query.get(collided_entity) {
        if !origin_hit_matrix.can_hit(origin_team, collided_with_team) {
            return;
        }
    }
        
    commands.entity(origin_entity).despawn();
}

Is this basically what other people are doing? I feel like it'd be more ergonomic if 2 mirrored events were fired for every collision, but I'm sure that'd have performance overhead.

sleek thicket
silk mauve
#

An interesting approach. Thank you

blazing sluice
#

@silk mauve this is my approach:

for (a, b) in [(event.0, event.1), (event.1, event.0)] {
hardy mason
#

hi, is there perhaps some way to temporarily exclude entities from the physics simulation? in my case, when the game actors enter vehicles, I want the actors to be invisible and sync the actor position to the vehicle position at all times, with collisions turned off. it should almost be as though they don't exist, without despawning them because i need some parts of the entities for game logic. things I tried so far: i removed the RigidBody component temporarily, but that keeps collisions going. I removed the Collider component temporarily, and that gives me warnings about low mass. I added the Sleeping component, but that only stops movement, not collision logic. any ideas?

patent vigil
#

maybe try adding sensor?

#

hmm maybe not if you want to keep it synced with vehicle tho

hardy mason
#

removing both RigidBody and Collider does the trick, but I'm getting some glitch with this, makes me wonder whether I'm doing the right thing.. (The glitch is: When the driver exits the vehicle and I add RigidBody back in, then any Transform/Visibility changes I do to the actor in the same frame don't seem to have any effect and get reverted almost immediately. But I can work around this~)

last panther
#

btw
what do you do if like
two objects have multiple collision points with each other?
Then you could end up like doubling the amount of impulse you apply or something right?
do you just like do a convex decomposition so that two objects can't collide at multiple points at once?

vestal minnow
# last panther btw what do you do if like two objects have multiple collision points with each ...

Multiple contact points can be very important for stability in many cases, like for stable cube stacks, for example. I haven't thought about this much, but I don't think you generally need to handle it in a special way. Even if the impulses were doubled, you typically wouldn't notice it other than maybe for large impacts.

The impulses do take the relative velocities of the bodies at the contact point into account, so applying an impulse for one contact point will also affect the impulses applied to other contact points.

last panther
#

ah

#

right that was a solution

#

hmm

#

I can't really do that though

vestal minnow
#

How are you solving contacts then? Afaik using the relative velocities is basically required

last panther
last panther
#

exert force proportional to penetration

vestal minnow
#

mm yeah

last panther
#

but I'm doing a like grid based thing
so there'll be lots of collisions between two objects at once instead of just 1 or 2
and idk what to do about that

vestal minnow
last panther
#

I'm applying the collision ignoring all other possible collisions since I have to run it in parallel
so yeah...

#

you can actually see in this image something like that I think

#

the second collision the velocity greatly increases

vestal minnow
#

might be a bit mathy though

#

I think this is the important thing

#

normalize the force by the number of contact points

last panther
#

yeah
I realized I was misreading the impulse solver paper and you should do multiple iterations of impulse solving anyways

#

and yeah that could work but I think I'll try impulse-based

last panther
last panther
#

@vestal minnow right I was reading it
and we have this impulse thing
but idk how should I add restitution to it
I guess just add a portion of the input velocity to the output?
going to have to some sort of thing which doesn't break with multiple steps though

vestal minnow
# last panther <@545959292281552928> right I was reading it and we have this impulse thing but ...

I think the approach Erin Catto takes in the new version of Box2D is to apply restitution in a separate loop after the normal and friction impulses:

// Pseudocode

let constraints = prepare_constraints();

for constraint in constraints.iter_mut() {
    constraint.solve_normal_impulses();
    constraint.solve_friction_impulses();
}

for constraint in constraints.iter_mut() {
    constraint.apply_restitution();
}

apply_restitution would do something like this:

// Iterate through the contact points for the constraint.
// Each point belongs to the same manifold.
for point in constraint.points.iter_mut() {
    // Relative velocity at contact point
    // Note: You might be able to cache this in the constraint
    //       since it's computed earlier for normal impulses.
    let relative_velocity = body2.velocity_at_point(r2) - body1.velocity_at_point(r1);
    let normal_speed = relative_velocity.dot(normal);

    if normal_speed > -threshold {
        continue;
    }

    let mut impulse = -point.normal_mass * (normal_speed + restitution * relative_velocity);

    // Clamp the accumulated impulse
    let new_impulse = (point.normal_impulse + impulse).max(0.0);
    impulse = new_impulse - point.normal_impulse;
    point.normal_impulse = new_impulse;

    // Apply contact impulse
    let impulse = impulse * normal;
    // ...
}

point.normal_mass is the "projected mass" along the contact normal:

let k_normal = inverse_mass_sum +
    inv_inertia1 * r1.cross(normal).powi(2) +
    inv_inertia2 * r2.cross(normal).powi(2);

point.normal_mass = if k_normal > 0.0 { 1.0 / k_normal } else { 0.0 };
#

I believe you could also modify the velocity bias based on restitution somehow

#

That might be closer to the implementation those slides have

#

But I think the old Box2D might do a position-based solve after the velocity solve

#

(while the new one is just a velocity solve, but substepped and with "soft constraints")

last panther
#

alright thanks!

vestal minnow
#

Running Solver2D locally now, noticing that warm starting makes a very large difference. XPBD is much more stable than any other method without warm starting, but with warm starting other methods (especially TGS Soft) outperform XPBD in almost all tests

#

Except TGS soft has more flex for chains

last panther
#

huh

#

warm starting requires having the same collisions in multiple frames right

vestal minnow
#

It stores the impulses from the previous frame yeah

last panther
#

but it also requires storing the collisions from previous frames hmm

#

which I can't reallllllly do

#

implemented it btw

vestal minnow
#

nice

last panther
#

only issue is that like in their thing they apply the impulse after every collision and I can't do that and have to sum it up across all collisions and then apply it after

#

which means that I have to divide the impulse by the number of collisions to prevent it from exploding

vestal minnow
#

yeah hmm

#

if it works well enough for your use case, then πŸ€·β€β™‚οΈ

last panther
#

eee

#

I'm consdering switching to xpbd then actually

#

since I can't do warm starting

#

wait, does xpbd apply each constraint in parallel or one after each other

vestal minnow
last panther
#

but there are still issues if you have multiple parallel constraints right?

vestal minnow
#

a Jacobi solve makes parallelization easier

#

(I haven't done it tho)

last panther
#

if I remember correctly the paper involves dividing by the number of constraints on each object??

vestal minnow
#

no?

#

I don't think so

#

for XPBD

last panther
#

well then what's preventing overshooting
if you have 2 collisions for the same object

vestal minnow
#

honestly not sure, but I haven't noticed it

last panther
#

ok hm

vestal minnow
#

yeah I guess it could be an issue with a Jacobi solve, hmm

last panther
#

yeah non jacobi solver you shouldn't have the issue

vestal minnow
#

ah yea that divides by the number of constraints

#

Gauss-Seidel style solvers can also be parallelized with graph coloring, but that's more complex

#

(also I gotta go for ~30 min)

last panther
#

oh no

#

any links to that?

#

alright I implemented just dividing by num constraints

#

seems to work decently well

vestal minnow
#

The general description of graph coloring is on Wikipedia
https://en.wikipedia.org/wiki/Graph_coloring

In graph theory, graph coloring is a special case of graph labeling; it is an assignment of labels traditionally called "colors" to elements of a graph subject to certain constraints. In its simplest form, it is a way of coloring the vertices of a graph such that no two adjacent vertices are of the same color; this is called a vertex coloring. S...

last panther
#

alright thanks
you doubled one of your links

definitely not going to implement this though, it seems a bit complex

vestal minnow
#

I probably wouldn't go through the trouble of implementing this unless you really need to yeah

last panther
vestal minnow
#

should be easy to test

last panther
#

I tried it

#

it explodes

vestal minnow
#

yup

last panther
#

..
and somehow crashes my computer

#

weird

#

I don't really get why it explodes though

#

since when you collide you compute the impulse to make the things equalize their velocities basically

#

..
and I'm still having an issue of interpenetrating the first frame during collision

vestal minnow
# last panther Actually now that I think about it Shouldn't you be able to apply restitution tr...

Naively deriving the impulse from physics equations; The coefficient of restitution determines the ratio of the relative velocity before and after the collision:

restitution = vel_after / vel_before

solve for vel_after

vel_after = restitution * vel_before

the delta is

vel_before + delta_vel = restitution * vel_before
             delta_vel = restitution * vel_before - vel_before

so the total normal impulse magnitude should be

total_impulse = delta_vel * mass
              = (restitution * vel_before - vel_before) * mass

(but in the direction of the normal)
so I guess the restitution impulse would be this?

restitution_impulse = total_impulse - contact_impulse

this is very likely wrong in practice for sequential impulses though, idk

last panther
#

what's contact impulse?

But yeah idk it seems right
I feel like I probably ended up double counting something somewhere or smth

vestal minnow
#

contact impulse is just the impulse you'd have without restitution added

#

so you subtract that from the total impulse you expect based on the coefficient of restitution to get the impulse that is applied to account for restitution

#

but yeah this naive approach probably wouldn't work

last panther
#

yeah hm

#

maybe because of multiple bodies?

#

wait hm

#

Maybe just apply the position update with the contact impulse and then apply the restitution?

vestal minnow
#

XPBD kinda does that

last panther
#

so you don't have issues with more interpenetration

vestal minnow
#

XPBD has a restitution velocity solve after the position-based solve

#

You just need to store the pre-solve velocities

blazing sluice
#

(2D) I'd like to have my enemies have parts that are armoured and vulerable spots. Is there a recommended way to go about this in xpbd? If I have a RigidBody::Dynamic, should I add children which are static that have their own local translation? Should I add root level entities with constraints to keep them stuck to the enemy?

vestal minnow
#

You could add the different spots as separate colliders which are children of the parent rigid body

#

Child colliders should follow the parent

blazing sluice
#

Aha, so the colliders don't need a RigidBody::Static or Dynamic?

vestal minnow
#

Nope

blazing sluice
#

Awesome thank you πŸ‘

last panther
#

hmm

#

also @vestal minnow I tried the simple restitution method again and it seems to work?

#

no explosions

#

im unsure about how much it conserves momentum tho

vestal minnow
#

yeah that could be a potential issue

#

would probably need to test it to see

little maple
#

I'm noticing sometimes the physics in my game feels like it pauses or skips every second or so... Like it's a really slight hiccup.. is that unusual?

last panther
#

might be fixed update

zinc talon
#

Easy fix is to set FixedUpdate to 60Hz, but for some games that's not an option

#

I have an RTS that depends on a set tick rate (50Hz) so the solution in that case would probably involve some weird interpolation and 1-frame rendering lag

little maple
#

oh, is FixedUpdate used by bevy_xpbd?

zinc talon
#

πŸ€”

#

Oh that's a good question

#

I think the default schedule is fixed update?

#

Nope

#

Unless you're manually setting the schedule ig

#

In that case, I have no idea what's making it skip like that

little maple
#

hmm.. might be something to look more closely at though

jagged hawk
#

Hello! I am using collisions to check if entities are touching or inside of each other. I have many, many entities but I only need to do this on-demand. Is there a way to turn off the collision system and only query specific collisions or collision layers?

carmine sluice
zinc talon
#

FixedUpdate 59Hz bavy

thin hare
#

Is there a way to have a LinearDamping component only apply laterally? or would i just need to roll my own out?

vestal minnow
thin hare
#

gothca thanks, I'm assuming it shouldn't be too hard for me to figure out, I'm guessing it's mainly just lerping the values towards 0.0 by whatever my damping value is

vestal minnow
vestal minnow
#

Bevy's FixedUpdate is 64 Hz, but with that I got some weird performance issues or even worse frame skips (don't remember) so I kept it at 60 Hz for now

#

Unity is the one with 50 Hz iirc

vestal minnow
zinc talon
little maple
#

hmm, so whatever visual hiccup I'm seeing may not be due to this fixed update if I'm hitting 60fps?

ivory pine
#

I seem to have a fundamental misunderstanding that I cant figure out.

Im trying to recreate the 3d_scene example with the falling cube.

I copy paste the code and it works as expected.

I want to make a change so that the cube is spawned when I click an egui button. I have a system that listens to a bevy event, and spawns the exact same cube, except this time it seems to move at like 1000x the speed, and i only see it flash on the screen for a frame.

The only difference here is that one system is in the Update step, where as the original example is in Setup.

EDIT: it seems like removing the angular velocity component fixes this. no idea why

dusty bolt
#

I'm Currently attempting to make a third person camera controller, with a sphere as a placeholder for a character or model etc. and I'm ray casting from the origin of the parent (the anchor point of the camera) out and then if there is a hit then setting the camera's position to the hit point, however right now the ray hits the sphere and so i have been trying to use layer masks and collision layers so the ray will skip the player mesh / collision, which i have code for, however it isn't working, and I'm just wondering what I'm doing wrong and also if i should attempt to do it another way, here is the responsible code:

(in the library i created to handle the camera stuff) - Lib.rs:

#[derive(PhysicsLayer, Clone, Copy, Debug)]
pub enum GameLayer {
    Player = 0b0001,
    Enemy = 0b0010, 
    Environment = 0b0100, 
}

fn cam_setup(mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>, 
    mut materials: ResMut<Assets<StandardMaterial>>,) {

        assert_eq!(LayerMask(0b0001), GameLayer::Player);

        let camera_mask = LayerMask::ALL & !LayerMask(GameLayer::Player as u32);
    
        assert_eq!(camera_mask, LayerMask(!0b0001));

commands.spawn((...RayCaster::new(Vec3::ZERO, Direction3d::Z, )
        .with_query_filter(SpatialQueryFilter::from_mask(camera_mask))
        ,
    CameraRay,
    ));

...

And then over in the main.rs where the player is set up:

    commands.spawn((...
    },
    ThirdPersonCameraTarget,
    InterpolatedPosition::from_source(physics_entity),
    InterpolatedRotation::from_source(physics_entity),
    CollisionLayers::new(GameLayer::Player, [GameLayer::Enemy, GameLayer::Environment, GameLayer::Player]),
    ));

i apologise that this is so long btw

cinder summit
vestal minnow
cinder summit
#

So then physics_entity would need the CollisionLayers no? πŸ€”

vestal minnow
#

Yep, I think that's probably the issue here

#

The interpolated entity should be used just for rendering, and all the physics/collision components should be on the physics entity

little berry
#

@vestal minnow
How does your crate deal with scale changes?
It seems to me that you either have to propagate transforms twice per frame (once before applying scale, once after integrating) or to have 1 frame delay on either integration or scale application.

dusty bolt
abstract tendon
#

so lets say i have character and i want it to jump so i need to check if its touching someting. is it a good idea to create a shapecast under the character collision and check if its touching something?

#

if its not a good idea, how should i do it?

sleek thicket
vestal minnow
#

So collider scaling might have a one-frame delay if you change the scale before physics is run

little berry
#

I see, thank you.

vestal minnow
#

The main problems with a ray cast would probably be that (1) if you go off of a ledge, you can't jump the moment the center of your character goes over the ledge unless you implement a manual Coyotee time jump, and (2) if you happen to be standing on some small crack in the ground and the ray happens to point at it, you can't jump

sleek thicket
#

coyote time is actually super simple
instead of making grounded a bool, you make a float and reset it to 0 ray hits ground, or add delta time if not. so when you jump, you just check if grounded < coyotetime

#

shapecast is fine if all you need to care about is that it hit at all. if you need normal for slopes then it's wacky, goes sideways on every triangle edge

cinder summit
# little maple hmm, so whatever visual hiccup I'm seeing may not be due to this fixed update if...

Looks like no one answered ... Hitting 60FPS doesn't actually mean your ticks happen exactly once a frame, if you get a 2/0/2 pattern it can be very easy to get hiccups. If it doesn't always happen it's probably either the ticks getting uneven like that or a system ordering issue. bevy's default FixedUpdate rate (64Hz) also has this problem, but the benefit there is that it consistently happens at least 4x per second (at 60FPS) so it won't suddenly catch you by surprise. If you run 2 ticks in 1 frame, chances are you also don't have input for both ticks, which can cause further hiccups

little maple
cinder summit
#

bevy_xpbd essentially just runs its own fixed update loop in PostUpdate by default (otherwise the physics become tied to FPS, which is of course very bad)

#

The 2/0/2 pattern happens because at 60FPS some frames might be just under 1/60th of a second and the next slightly over, and that can cause the first one to run 0 ticks, and the next to run 2

little maple
cinder summit
#

You can't prevent it but you can do things to make sure there's always input and no visual hiccups appear

ionic gale
#
    input: Res<ButtonInput<KeyCode>>,
    mut players: Query<&mut ExternalForce, With<Player>>
) {
    let input_x: f32 = (input.pressed(KeyCode::KeyD) as i8 - input.pressed(KeyCode::KeyA) as i8) as f32;

    for mut player_force in players.iter_mut() {
        player_force.apply_force(Vec2::Y * input_x * PLAYER_SPEED);
    }
}```Should this work?
cinder summit
ionic gale
#

I don't know why.

cinder summit
#

Hmmmm ... Could be that force doesn't affect your player entity at all, due to some missing component or something πŸ€”

vestal minnow
#

at least ExternalForce should be added automatically

ionic gale
vestal minnow
#

You could try adding logs in the system to make sure it's running properly

fn player_input_system(
    input: Res<ButtonInput<KeyCode>>,
    mut players: Query<&mut ExternalForce, With<Player>>
) {
    let input_x: f32 = (input.pressed(KeyCode::KeyD) as i8 - input.pressed(KeyCode::KeyA) as i8) as f32;

    for mut player_force in players.iter_mut() {
        println!("{}", input_x);
        player_force.apply_force(Vec2::Y * input_x * PLAYER_SPEED);
    }
}
#

Also maybe the force is just way too small?

ionic gale
vestal minnow
#

You could try increasing it a bunch, like multiply by 100

ionic gale
cinder summit
#

If Force per frame or per second? πŸ€”

ionic gale
vestal minnow
vestal minnow
# ionic gale I set speed to 128.

How big is the player collider? This is 2D, so if you're using pixels as units, you might need very large forces to move them meaningfully

cinder summit
#

Is there anything in contact with the collider? Maybe friction ... Or it could be another system overwriting things ... That spawn is missing visibility and global visibility (cause it's TransformBundle and not SpatialBundle) so I'd imagine something adds more component (probably based on the Player marker?), and maybe that keeps overwriting the position?

vestal minnow
#

(assuming 1 pixel = 1 meter and a density of 1 here)

abstract tendon
#

ive got a problem, when i move my character everything looks is fine, when i turn my camera everyhing looks is fine, but when i move and turn at the same time its jittery.
I move character with changing linear velocity of player.
camera is the child of player, so i rotate player on y instead of rotating camera

#
app.add_systems(
    PostUpdate,
    camera_follow_player
        .after(PhysicsSet::Sync)
        .before(TransformSystem::TransformPropagate),
);

ive tried this solution on docs.rs but its still the same

#

so i dont know what to give you for you to help me better

vague pebble
#
  • The second you are utilizing transform rotation on a RigidBody. Guessing that ball. The correct way would be to use angvel with interpolation
#

Or torque impulses

#

If you would like I have a function that rotates colliders and rigid bodies without jittering

abstract tendon
#

Damn i would like to see it

vague pebble
#

So first sample video, just to check if this is what you want

#
    cam_q: Query<&Transform, With<CamInfo>>,
    mut vel: Query<&mut Velocity>
    ){

    let mut current_time = 0.0; // Current time, starting from 0
    let total_s = 0.5; // Max s value of interpolation in seconds
    let dt = 1.0/60.0; // Time step for interpolation, adjust as needed

    let current_q = player_q.get_single().unwrap().rotation.normalize();
    let target_q = cam_q.get_single().unwrap().rotation.normalize();
    for mut v in vel.iter_mut(){
        
        while  current_time < total_s {
    
            let s = current_time / total_s;
    
            let interpolated_q = current_q.slerp(target_q, s);
    
            let q_difference = interpolated_q * current_q.inverse();
    
            let (axis,angle) = q_difference.to_axis_angle();
     
            let angvel = (axis[0] * angle / dt, axis[1] * angle / dt, axis[2] * angle / dt);

            v.angvel = angvel.into();
    
            current_time += dt;        
        }

    }
}```
#

Here is the code

#

As you can see i am moving player which is a rigid body only using velocity

#

The slight delay you may notice is something i left purposefully to have that smooth transition you can configure that

#

Oh btw that was written using rapier but I think the only difference is angvel is disassociated

abstract tendon
#

imma try it

vague pebble
#

If you want no jitter at all just increase dt and your interpolation will go quickly

kind lava
#

Is there a shortcut to remove all physics components from an entity that I want to no longer participate in the simulation?

dark hound
#

Is it possible to use SpatialQuery with another query that requires &mut Position? I'm running into conflicting accesses but I can't modify Transform directly as I have transform_to_position: false in SyncConfig

vestal minnow
#

At one point I had the intention of removing the SpatialQuery system parameter in favor of the resource (maybe renaming that to SpatialQuery?), which would fix all conflicts like this, but I either had some issue with that or just never got around to doing it... I might revisit it after the solver rework

#

Currently SpatialQuery just duplicates all the APIs and forwards method calls, which is rather pointless. The only extra thing it has is an update_pipeline method which requires ECS access, but most users don't need that, and it could just be made a separate system or something

vestal minnow
#

Some components are intended to be more "internal" and might even be private currently, so it might not be straightforward to cleanly remove all physics components from the outside

#

(without despawning the entity of course)

dark hound
fringe mango
#

recently noticed that an entity spawned with a collider initially has a bigger collider which then becomes the proper size, anyone else experiencing this issue?

vestal minnow
#

If that's the case, I think it should be pretty easy to fix on the bevy_xpbd side

ionic gale
#

If I want to make my physics object a sprite do I just add the sprite bundle to it?

vague pebble
#

Anyone has attempted to make a hair that is affected by physics yet? Just curious

cinder summit
ionic gale
vestal minnow
# ionic gale It threw me an error.

Do you have duplicate components or something? For example, SpriteBundle has a transform already, so you should set that directly instead of adding your transform outside the bundle

ionic gale
vestal minnow
#

Could you show the code for how you spawn the entity?

ionic gale
little berry
#

Did anyone run bevy_xpbd through a debugger? I'm using vscode LLDB, and I'm getting trash data in the variables with the default optimization on 1.

vestal minnow
#

I don't think I have personally; what do you mean by trash data?

little berry
#

The dbg!() blocks show data different from the data shown in the debugger view. I was implementing a solver, and the debugger was showing tangent_impulse as 5000, and normal_impulse as 0. But then prints the opposite to the console.

#

Maybe it's to do with the weird workspace setup for the crate.

#

I test 2d and 3d by using a single member.

#

Otherwise it doesn't compile.

vestal minnow
# ionic gale I think that is added in the physics bundle.

If you have two bundles and they both add the same component, you can't add them in the same method call, but you can insert the other bundle separately.

// Here, the variables represent the bundles you want to add.

// If they both have (for example) Transform, you can't do this:
commands.spawn((
    sprite_bundle,
    physics_bundle,
));

// Instead, do this:
commands.spawn(sprite_bundle).insert(physics_bundle);
vestal minnow
#

I'm not sure if that's something I can really fix? Although if it is related to the weird crate setup (which Rapier also uses) for whatever reason, then we could try adding a shared "core" crate like I proposed here
https://github.com/Jondolf/bevy_xpbd/issues/298

#

Unless it's specifically related to using workspaces, but I really doubt that since they're quite common in Rust

#

Bevy itself has all of its crates in a large workspace

little berry
#

I've honestly not had a good time debugging with bevy in general. The data usually just doesn't show up at all, which I thought was the problem with bevy getting optimized, but it was never showing garbage. So I got surprised.

vestal minnow
#

So it gives wrong results even with a single member?

little berry
#

Single member is the only way I can build bevy_xpbd, so yeah.

#

Otherwise I think rust tries to compile both 2d and 3d, but the shared root crate can only support one.

vestal minnow
#

Interesting, for me it works fine (also using VSCode)

little berry
#

hmm, i pulled from master

#

maybe I did something else wrong

vestal minnow
#

For me, it has always chosen either 2D or 3D, but not both at the same time

#

(which to be fair is a bit annoying when you specifically want the other dimension to be processed by Rust Analyzer)

#

I believe symlinks could be used to make it nicer but it might also be even more confusing and I'm not sure if all platforms support them

little berry
#

Yeah, should probably not complicate it further.

vestal minnow
#

IIRC NPhysics used symlinks but Rapier doesn't

vestal minnow
#

I haven't personally used them much so I don't have a lot of experience

little berry
#

Maybe I'll get annoyed enough at some point and create an issue for it xd

ionic gale
#

As for the code it is:// Player let color = "Red"; let shape = "round"; let playerTexture = assetServer.load("textures/character_".to_owned() + shape + color + ".png"); commands.spawn(( RigidBody::Dynamic, Collider::capsule(50.0, 20.0), TransformBundle { local: Transform { translation: Vec3::new(0.0, 200.0, 0.0), ..default() }, ..default() }, ExternalForce::new(Vec2::Y).with_persistence(false), SpriteBundle { texture: playerTexture, ..default() }, Player ));

ionic gale
#

Figured it out.

#

Thanks @vestal minnow!

ionic gale
#
let color = "Red";
let shape = "round";
let player_texture = asset_server.load("textures/character_".to_owned() + shape + color + ".png");
commands.spawn((
    RigidBody::Dynamic,
    Collider::capsule(42.0, 28.0),
    ExternalForce::new(Vec2::Y).with_persistence(false),
    SpriteBundle {
        texture: player_texture,
        transform: Transform {
            translation: Vec3::new(0.0, 200.0, 0.0),
            ..default()
        },
        ..default()
    },
    Player
));```How do I lock the rotation?
ionic gale
#

Sorry for the ask.

#

I did try to figure it out on my own.

dark hound
#

no problem! glad I could help

ionic gale
#

What tilemap should I use with this?

#

Or how would I create a collider based on an svg image?

ionic gale
#

I am going to try and figure out how to use the polyline thingy.

ionic gale
#

Could someone please explane the polyline because I am not getting it.

ionic gale
#

I think that I just figured it out!

ionic gale
#

Is there a concave shape that I could use?

vestal minnow
# ionic gale Is there a concave shape that I could use?

Collision detection algorithms don't generally work for concave shapes directly, but you can often represent concave shapes using several convex shapes (this is known as "convex decomposition"). You can make a collider as a combination of shapes using Collider::compound, or in 3D you could also make a collider from a mesh

#

2D also supports polyline colliders

ionic gale
#

How would I automatically make several convex shapes based on a concave shape?

vestal minnow
#

so that can be concave

normal anchor
#

I'm having an issue, when I spawn my entity. In the Setup schedule the collider is being created fine but when I do it in the Update schedule no collider is created.

The picture I attached shows 4 chunk entities, the bottom right was created at Setup and the rest at Update. As can be seen only the bottom right one has a collider.

Below is the code that I use for creating the entity:

        let raw_mesh = chunk_to_mesh(self);
        let mesh = meshes.add(raw_mesh.clone());
        let mut chunk_entity = commands.spawn((
            ChunkBundle {
                chunk: Chunk,
                mesh: MaterialMeshBundle {
                    mesh,
                    material: voxel_material.clone(),
                    transform: Transform::from_translation(Vec3::new(
                        self.world_position.x as f32 * self.size.x as f32,
                        self.world_position.y as f32 * self.size.y as f32,
                        self.world_position.z as f32 * self.size.z as f32,
                    )),
                    ..Default::default()

                },

                rigid_body: RigidBody::Static,
                collision_layers: CollisionLayers::new(
                    RigidLayer::Ground,
                    RigidLayer::Player,
                ),
            },
            raw_mesh.compute_aabb().unwrap(),
            AsyncCollider(ComputedCollider::TriMeshWithFlags(
                TriMeshFlags::MERGE_DUPLICATE_VERTICES,
            ))
        ));
vestal minnow
#

AsyncColliders are currently initialized in Update

#

Although with the way the system works, I don't see why that would matter πŸ€”

normal anchor
normal anchor
vestal minnow
#

Interesting... for now, you could probably handle the collider creation manually

        let raw_mesh = chunk_to_mesh(self);
        let collider = Collider::trimesh_from_mesh_with_config(
            &raw_mesh,
            TriMeshFlags::MERGE_DUPLICATE_VERTICES,
        );
        let mesh = meshes.add(raw_mesh.clone());

        let mut chunk_entity = commands.spawn((
            ChunkBundle {
                chunk: Chunk,
                mesh: MaterialMeshBundle {
                    mesh,
                    material: voxel_material.clone(),
                    transform: Transform::from_translation(Vec3::new(
                        self.world_position.x as f32 * self.size.x as f32,
                        self.world_position.y as f32 * self.size.y as f32,
                        self.world_position.z as f32 * self.size.z as f32,
                    )),
                    ..Default::default()

                },

                rigid_body: RigidBody::Static,
                collision_layers: CollisionLayers::new(
                    RigidLayer::Ground,
                    RigidLayer::Player,
                ),
            },
            raw_mesh.compute_aabb().unwrap(),
            collider,
        ));
normal anchor
vestal minnow
#

Yeah, it's very weird... All the system does is query for meshes for entities with AsyncCollider, build the colliders for those entities, and remove the AsyncCollider component so that the system doesn't run again for those entities the next frame

#

and there are no other query filters or run conditions

normal anchor
#

In that case I might have another lead, the AsyncCollider component stays on the entity when I look at it using the egui inspector

vestal minnow
#

I think that would indicate that the entity doesn't have a Handle<Mesh> πŸ€”

#

The system is just this

pub fn init_async_colliders(
    mut commands: Commands,
    meshes: Res<Assets<Mesh>>,
    async_colliders: Query<(Entity, &Handle<Mesh>, &AsyncCollider)>,
) {
    for (entity, mesh_handle, async_collider) in async_colliders.iter() {
        if let Some(mesh) = meshes.get(mesh_handle) {
            let collider = match &async_collider.0 {
                ComputedCollider::TriMesh => Collider::trimesh_from_mesh(mesh),
                ComputedCollider::TriMeshWithFlags(flags) => {
                    Collider::trimesh_from_mesh_with_config(mesh, *flags)
                }
                ComputedCollider::ConvexHull => Collider::convex_hull_from_mesh(mesh),
                ComputedCollider::ConvexDecomposition(params) => {
                    Collider::convex_decomposition_from_mesh_with_config(mesh, params)
                }
            };
            if let Some(collider) = collider {
                commands
                    .entity(entity)
                    .insert(collider)
                    .remove::<AsyncCollider>();
            } else {
                error!("Unable to generate collider from mesh {:?}", mesh);
            }
        }
    }
}
little berry
#

Worth spamming a warning if mesh is missing here IMO.

vestal minnow
#

Yeah, true

normal anchor
#

Mesh is not missing

#

And indeed for the Setup entity the AsyncCollider is not present

little berry
#

This is the problem?

#

Not sure how assets work

vestal minnow
#

Yeah it needs to point to an asset in Res<Assets<Mesh>>

normal anchor
#

That's what I thought in the beginning too but check this image of the working entity

little berry
#

oof

normal anchor
#

I'm using the standard way of adding meshes:
let mesh = meshes.add(raw_mesh.clone());

vestal minnow
#

Tbh I have no idea what's happening there... I don't see how Startup would differ from Update here

normal anchor
#

Ok I did some more debugging and looking at the init_async_colliders function and I added a system to my game that would just try to get the mesh fromt the entity and it's indeed failing. This is probably not the place to ask for it but do you might know the reason for the fact that my mesh can't be found in the Assests?

This is the code I used for checking and my console is being spammed with AAAAAAAa

pub fn update_chunk_colliders(
    meshes: Res<Assets<Mesh>>,
    chunks: Query<(Entity, &Handle<Mesh>), (With<Chunk>)>
) {
    for (entity, mesh_handle) in &chunks {
        let mesh = match meshes.get(mesh_handle) {
            Some(mesh) => mesh,
            None => {log::info!("AAAAAAAa"); continue},
        };
        log::info!("FOund mesh: {:?}", mesh);
    }
}
#

And above in the code for generating the entity I shown how I'm adding it

vestal minnow
#

Unless you remove the mesh from assets somewhere or replace the handle or something, then no I don't really know...

#

It's strange that it's still rendering it even though the asset seemingly doesn't exist

normal anchor
#

Exactly, it's in a weird state of existing and not existing

vestal minnow
#

SchrΓΆdinger's chunk

normal anchor
#

Thank you for the help πŸ™‚
Your crate is super awesome and intuitive to use

vestal minnow
#

No problem, and thanks πŸ˜„ lmk if you figure out the mesh weirdness

normal anchor
#

Will do!

#

I figured it out, I forgot that I set my mesh to RenderWorld so it would be removed no matter what when entering RenderWorld. Changing it to:
let mut mesh = Mesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::RENDER_WORLD | RenderAssetUsages::MAIN_WORLD);
Solved it

vestal minnow
#

Ah, yeah that makes sense. Nice that you figured it out!

normal anchor
#

Thanks

silk mauve
#

Has anyone played around with collider generation? I'm trying to get my VHACDParameters for my procedural terrain. Currently the generation near cliffs is incredibly inconsistent, leading to invisible "ramps" near the sides of cliffs. Here's a screenshot that hopefully illustrates what I mean.

#

Notice my character is floating off the ground and the collider only vaguely follows the cliff.

#

Am I approaching this incorrectly?

vestal minnow
ionic gale
little berry
#

@vestal minnow What's your plan for joint improvements?

#

Gonna do generic 6dof like rapier or something else?

vestal minnow
# little berry Gonna do generic 6dof like rapier or something else?

I might explore that option too, but Rapier's generic joints are quite complex (I'd need to really study them) and might rely on some Nalgebra features that we don't have. I haven't looked at the implementation too in-depth yet, but I feel like manual implementations could also be stabler and faster than a fully generic solution. That needs to be tested though.

For the new solver, I'm currently thinking that I might keep XPBD for joints for now, since it's quite stable and straightforward. But the XPBD paper's way of handling joints isn't ideal, so I'm now trying out matrix-based implementations like what Bepu has. So essentially, I'm trying to do 3D joints similarly to Bepu and 2D joints similarly to Box2D, but with XPBD logic.

#

Locally I have a working implementation of the matrix-based block solve for 2D and 3D point-to-point constraints (used by most joints), and it's significantly less stretchy and jittery than the old impl

#

I also fixed a 2D joint "explosiveness" issue caused by denormalized rotations

#

As for other planned improvements:

  • Joint motors and servos
  • Nicer tuning parameters, like a damping ratio and frequency, or CFM and ERP, instead of just XPBD compliance
  • Joint breaking based on some force threshold
  • Nicer component-based API (relations would be nice tho)
#

A generic 6DOF joint isn't off the table either though, and I think it could be nice to have even if the built-in joints have custom implementations

#

And yeah I'll probably try impulse-based joints too

vestal minnow
little berry
#

That's a lot of things to try!

#

My biggest gripe with rapier and bxpbd so far have been joint limits not behaving properly. I tested my ragdoll setup in unreal, and it worked fine. Hopefully it's not too hard of a fix.

vestal minnow
#

Yeah, I haven't tried Rapier's joint limits in a while, but XPBD's definitely has some issues. I did fix one bug with 2D revolute joint limits and made a working ragdoll with that. But some things are likely buggy still

#

I'll probably make the stiffness of the limits configurable as well

little berry
#

Hopefully easily extendable/replacable. I think I may need some custom joint limit behavior in the end.

#

Did you figure out an API for custom solvers?

vestal minnow
#

The current API with XPBD supports custom constraints already, and I've made some changes/improvements locally too

#

But supporting fully custom solvers isn't as trivial

little berry
#

Understandable.

#

On the topic of 6dof: I don't think the linear and angular components are dependent in any way, are they? Could be easier to reason with 2 3dof constaints.

vestal minnow
#

Hmm, potentially yes, but I imagine the constraints would be able to reuse some values so it'd be more efficient for it to be one constraint

#

Joints typically compute an "effective mass" which is used for computing the linear and angular impulse or correction

#

Different parts of that effective mass matrix apply to different DOFs, and you could probably store them separately, but I'm not sure if you can do all linear and angular computations fully separately, at least as efficiently

vestal minnow
fringe mango
little berry
#

Is substepping inherently dangerous?
You can use as many iterations as you want, and it will be fine, while substeps in bevy_xpbd don't update the broadphase, and in Eric's TGS Soft, don't even update the narrow phase, only penetration.
That seems like it would lead to missed collisions the more substeps you use.

vestal minnow
# little berry Is substepping inherently dangerous? You can use as many iterations as you want...

No, not really.

With some things left out, the simulation loop with iterations (PGS) looks like this:

broad_phase();
narrow_phase();

integrate_velocities(delta_time);

for _ in 0..iterations {
    solve_constraints_with_bias(delta_time);
}

integrate_positions(delta_time);

for _ in 0..iterations {
    // Relax
    solve_constraints_without_bias(delta_time);
}

finalize_positions();

And with substeps (TGS), it looks like this:

broad_phase();
narrow_phase();

let h = delta_time / substeps;

for _ in 0..substeps {
    integrate_velocities(h);
    solve_constraints_with_bias(h);
    integrate_positions(h);
    solve_constraints_without_bias(h);
}

finalize_positions();

(of course you could have iterations within substeps too)

In both cases, the bodies are moved by position integration the same amount between narrow phase runs. With substeps, the movement is just split into smaller individual chunks instead of advancing bodies a ton at once like when using just iterations. So in that aspect, the amount of "missed collisions" is the same.

The current bevy_xpbd actually does run the narrow phase in the substepping loop, and the broad phase accounts for running just once a frame by expanding AABBs based on velocity. This AABB expansion is suggested by the XPBD paper.

However, running the narrow phase in the substepping loop is horrible for performance. It's just not very viable. The new solver runs it outside the loop like Box2D, Rapier, Bepu... basically every engine.

To account for position changes during substepping, contact data is stored in local space and transformed to match the current pose when solving constraints. To account for missed collisions at fast speeds (tunneling), you'd use CCD, just like when not using substepping.

vestal minnow
little berry
#

In both cases, the bodies are moved by position integration the same amount between narrow phase runs.

I see! That makes sense. Thank you.

blazing sluice
#

What looks like an xpbd interpolation bug of some sort. Hard to reproduce because this is after ~10^7 frames. This is showing a discrepancy between the game objects and the PhysicsDebugPlugin.

vestal minnow
#

Yeah I've noticed this as well, could be some rotation denormalization issue? It's hard to tell

blazing sluice
#

Hmm, this is in 2D, I see you're storing rotation as the sin and cos of the angle, those fields are private and there isn't any provision to renormalize them, I think you may be right

vestal minnow
#

Yeah I have local changes where 2D rotations are normalized by the solver, and don't remember seeing this issue with that change, but it's not particularly trivial to reproduce

blazing sluice
#

I'll try and come up with a test case

#

I think it's coming down to repeated application of this

    /// Multiplies the rotation by another rotation. This is equivalent to adding angles.
    pub fn mul(&self, rhs: Self) -> Self {
        Self {
            cos: self.cos * rhs.cos() - self.sin * rhs.sin(),
            sin: self.sin * rhs.cos() + self.cos * rhs.sin(),
        }
    }
vestal minnow
#

To normalize, you'd basically do this

let length_recip = Vec2::new(rot.sin(), rot.cos()).length_recip();
let normalized = Rotation::from_sin_cos(
    rot.sin() * length_recip,
    rot.cos() * length_recip
);
#

(locally I have helpers for this)

blazing sluice
#

Gotcha, I'll try that, every N frames replace the Rotation component with a normalized copy. Thanks, will report back later!

vestal minnow
blazing sluice
#

Or maybe I'll make my mobs take damage over time proportional to how denormalized their rotation vectors are :D

blazing sluice
vestal minnow
#

Thanks for testing it, good to get confirmation on the issue :)

#

That'll be fixed in the next release

royal helm
#

Hmm, does 2/0/2 happen in practice without vsync?

cinder summit
little berry
#

I noticed this crate uses Vec3::Z for some of its forward defaults. It should probably follow bevy's NegZ forward convention.

oblique cloak
#

Is there a simple way to alter the type of a RigidBody (from Dynamic to Kinematic) from a RigidBodyQuery ?

little berry
oblique cloak
vestal minnow
#

I guess the revolute joint aligned axis is one

#

And perhaps some of the examples

little berry
#

Yeah, I was looking at joints when I posted that.

#

What does this do in the revolute joint apply_angle_limits?

            let limit_axis = Vector3::new(
                self.aligned_axis.z,
                self.aligned_axis.x,
                self.aligned_axis.y,
            );
vestal minnow
#

That's wrong, I have it fixed locally

little berry
#

Could you explain what you thought when you made it initially, and how you fixed it? I'm trying to compare it to the XPBD paper.

vestal minnow
#

It's meant to be this, basically

// [n, n1, n2] = [a1, b1, b2], where [a, b, c] are perpendicular unit axes on the bodies.
let a1 = *body1.rotation * self.aligned_axis;
let b1 = *body1.rotation * self.aligned_axis.any_orthonormal_vector();
let b2 = *body2.rotation * self.aligned_axis.any_orthonormal_vector();
let dq = angle_limit.compute_correction(a1, b1, b2, PI);
#

oh main doesn't have Mul for rotations either πŸ˜… I have a lot of unreleased changes locally, I'll probably start making PRs for them soon

little berry
#

Another thing, fixed joint on main cannot allow for arbitrary orientations of bodies.

#

Rapier makes it work with local frames on the joint as opposed to just anchors.

vestal minnow
little berry
#

Understandable xD

vestal minnow
little berry
#

Local frames can probably unify some joint code too.

#

As they can be used in both spherical and revolute joints.

vestal minnow
#

yeah it should be pretty easy to add

vestal minnow
little berry
#

Oh yeah, and spherical joint is missing individual swing axes, seemingly.

#

I don't easily get what the paper is saying for that joint.

vestal minnow
#

I should really test the spherical joint limits properly and compare with some other engine

little berry
#

Unreal has a very comprehensive joint system. Their gizmos show all the info about the limits.

#

And another thing. Joints should be able to specify that no collisions should happen between jointed bodies.

vestal minnow
#

PhysX calls both axes swing axes, and Jolt calls them twist axes πŸ€” Meanwhile XPBD has both

little berry
#

Unreal has Swing1, Swing2 and Twist.

#

Which makes the most sense to me.

#

If you have a long rod, it can swing about the 2 axes, and twist about its length.

vestal minnow
#

(relations when 🌈)

little berry
#

And another another thing. Joints should be allowed to be put on the bodies.

#

I don't think anything is blocking that? Only the Without<RigidBody>, from my understanding.

vestal minnow
#

Yeah, should be doable. And I guess you'd only need to give one Entity in that case since the other one would be inferred

little berry
vestal minnow
#

I think some other engine has this

little berry
#

Wouldn't that limit the number of joints for a given body, if you only allow joints to be on a body?

#

Since components cannot have duplicates.

vestal minnow
#

No, both would be allowed

#

On a body or as a separate entity

little berry
#

Maintenance hell D:

vestal minnow
#

maybe :D we'll see

vestal minnow
#

This (and a lot of other logic) would be nicer with a more component-focused approach to joints

#

I'll try to find my MVP from some time ago

#

Roughly like this #1124043933886976171 message

#

Each joint with collisions disabled could just have a CollisionDisabled marker component

vestal minnow
#

Anyways, I'll work more on the joint stuff very soon, I'm wrapping up CCD now

vestal minnow
vestal minnow
little berry
vestal minnow
#

The normal joints will still keep custom implementations though, for now at least

#

custom impls can most likely be more efficient and stable

little berry
#

I see 6dof as a niche thing.

vestal minnow
#

yep

little berry
#

It's nice to have for some uses, but most people just need the basic joints.

vestal minnow
#

yeah... looks nice in a list of supported features though lol

little berry
#

For sure.

vestal minnow
#

I think I got the 2D version (4DOF) working, it can at least reproduce revolute and prismatic joint behavior... Although for weirder configurations it's harder to figure out what the expected behavior even is, like what's a prismatic joint that also allows some rotation πŸ˜‚

#

or hmm, that might be like a pin-slot thing

#

yeah

#

I really need proper gizmo rendering for joint limits

little berry
#

Wish bevy had solid gizmos. I fear joint gizmos will be confusing with outlines.

vestal minnow
#

Yeah... Could also use a mesh, but that's more painful to manage

little berry
#

I see that joints constrain rotation first, then position, this makes sense because position depends on rotation. But then angular limits are enforced after, modifying rotation after positional correction. That seems incorrect to me.

vestal minnow
#

It might be kinda wrong, yeah. I think I originally used this project as a reference for the joints, and got the order from there

#

I'll try if it's better with the angular limits before the positional correction

fiery mortar
#

Hey everyone, when using linear velocity instead of transform the movement is kind of blurry is this normal or am I missing something. I'm using 3d and the camera isn't moving.

vestal minnow
#

I'll still change it to be before though since it seems more "correct" to me

little berry
#

It shouldn't make a visual difference at non-extremes, yeah.

#

But should be stabler with strong forces.

vestal minnow
#

Yeah

fiery mortar
#

Maybe jittery is a better word? It looks like it running on less fps

vestal minnow
#

Is it on every frame or every few frames? Wondering if it could be related to this 2/0/2 pattern #1124043933886976171 message

#

Physics runs at a fixed 60 Hz by default

fiery mortar
#

OK that might be it thanks, I'm running at 144 fps.

little berry
#

Try different timestep modes/values

fiery mortar
little berry
#

Local frames for joints are a bit awkward. For the fixed joint it makes perfect sense. But for the revolute joint the aligned axis workflow is a lot nicer. You just need to separate the axis into 2, one for each body, and you will get (almost?) all the freedom of the local bases.

#

For spherical joint you can construct the basis from the limit axes provided by the user. For the revolute the same is probably a good idea too.

vestal minnow
#

Yeah, I believe all that is pretty close to what Rapier does too, although for the revolute joint it only seems to allow one hinge axis instead of one on both bodies

#

(Bepu allows two though)

little berry
#

Probably an oversight.

#

It's not any different calculation wise, I think.

vestal minnow
#

Yep, I believe it should be basically identical

little berry
#

On second thought, requiring the user to provide orthonormal axes for the spherical joint might not be very good. Could just ask for the whole orientation and tell them where the swing and twist axes should be?

vestal minnow
#

Yeah I think usually engines just ask for the basis orientations and name which axes are which

#

Bepu seems to have swing, twist, and the point constraint as separate, and asks for basis orientations for twist and swing axes for swing

#

Rapier just asks for local frames

#

I think PhysX also asks for frames, and defines X as the twist axis that has the limit cone

#

For the generic 6DOF joint, I got all translational DOFs and revolute joint -like angular DOFs (1 unlocked, 2 locked) working properly. 2 unlocked and 1 locked (universal joint) also seems to mostly work, but the limits are currently unstable at some angles, potentially due to singularities or just some bugs. And for spherical joints, I haven't figured out elliptic cone limits quite yet

#

(interestingly, Jolt also supports pyramidal limits)

little berry
#

They wouldn't form a cone, but a pyramid naturally.

little berry
#

Btw, Jondolf, the mod.rs usage is not recommended in leu of *mod_name*.rs by the rust book.

#

Maybe a good idea to change at some point

vestal minnow
little berry
#

Didn't realize bevy used mod.rs Wonder why they chose it

#

Having the mod file be among the modules of itself always felt weird to me

#

Also I noticed you have the spherical joint available with 2d feature.

#

Should probably only have it for 3d

vestal minnow
# little berry Having the mod file be among the modules of itself always felt weird to me

For me it's the opposite, it feels strange that a module would be outside its own folder. And I don't want the module names to all be duplicated in the directory for the folder and file, especially since folders are typically at the top and files at the bottom, which creates a disconnect.

Like, this:

collision/
dynamics/
geometry/
math/
lib.rs

is nicer than this:

collision/
dynamics/
geometry/
math/
collision.rs
dynamics.rs
geometry.rs
lib.rs
math.rs

(I omitted the mod.rs files from the first one of course, but when traversing e.g. GitHub, you also don't see them before opening the folder)

In the latter one, the module files are disconnected from their own folders, which I don't like. And it gets worse with larger directories, and in the mix there could also be modules that don't have sub-modules, so then you can't immediately tell which modules are "parent" modules and which are "standalone" modules (for lack of a better term).

The main benefit to *mod_name*.rs in my eyes is that you can search for the files better and you don't get a bunch of different yet indistinguishable mod.rs tabs in editors, but I see that more as a tooling issue.

little berry
#

I did feel similar when I started with rust, duplicates of the same module name are cringe in the directory structure

vestal minnow
little berry
#

Nice

#

Another nitpick.
On main, joint limit torque is a vec3 in 3d, but they represent a single axis, and can be Scalars

royal helm
#

I also heavily prefer mod.rs personally, I'm not entirely sure why the other way is recommended

#

It feels weirdly disconnected and maybe thats just my familiarity with the language before the other option existed but meh

vestal minnow
little berry
#

If the basis of a body in a joint is it's rotation relative to identity, then doesn't it make sense to have only one basis for a joint? Why does rapier have 2?

#

So far I've only ever needed to specify one basis

abstract tendon
#

but i guess i will still use old method

blazing sluice
#

don't need help today, got body chunking working!

vestal minnow
blazing sluice
#

xpbd works very nicely with lots of child colliders, very impressed how the center of mass just gets handled for you

vestal minnow
#

It was quite painful to get working and the code is still rather questionable in some places... but I'm glad it works in the end πŸ˜…

blazing sluice
#

It seems to be very robust! Only had issues when spawning debris entities automatically, need a pre-spawn check for overlapping colliders so as to not spawn the debris if it's going to blow up the sim

wooden beacon
#

@vestal minnow How much do you expect external API's to change with the new solver? I've been kind of putting off digging into xpbd because of the imminent change

vestal minnow
# wooden beacon <@545959292281552928> How much do you expect external API's to change with the n...

The core API uses the same components and resources, so that won't really change. The module structure (and some docs) will change quite a bit, but that shouldn't matter if you use the prelude. Joints will most likely get upgrades, but again the general API should largely stay the same.

The biggest changes will be in the internals, so if you relied on some internal system ordering or some specific system sets, there might be breakage. And of course physics behavior might be slightly different (notably, collisions will be a bit "softer" but much more stable and performant).

#

In terms of breaking changes from a user's perspective, it'll be roughly like a normal bevy_xpbd release. There will be a migration guide

#

I'm not yet entirely sure how I'll handle the next release; I could just release everything under the new crate name (whatever it'll be), or I could first make one more bevy_xpbd release with everything except the new solver to ease the transition

jovial herald
#

Is there a way to turn debug rendering on and off globally at runtime?

vestal minnow
jovial herald
#

cool perfect

#

If I wanted to write a generic function to be used in multiple places to do this what would be the most ideomatic way do you think?

vestal minnow
#

just something like this

fn my_system<G: GizmoConfigGroup>(mut store: ResMut<GizmoConfigStore>) {
    let (mut config, config_group) = store.config_mut::<G>();
    // ...
}
#

or specifically a function and not a system?

jovial herald
#

I meant more like, I want to toggle_debug_render from systems for ui, bevy_console, possibly a keybind

#

but I can't just call a system from another system

vestal minnow
#

hmm, maybe a custom command?

#

or alternatively some system parameter

jovial herald
#

yeah, custom command or an event I think

#

come to think of it, maybe what I should do try to make some changes to how bevy_console works, so that the commands actually implement Command

#

and then use that as an abstraction layer

vestal minnow
#

hm, I'm not sure if commands can actually access resources

#

cursed solution, I wonder if you could instead run a one-shot system from the command thonk

jovial herald
#

I don't think its that cursed

little berry
#

Since there are commands to run systems, I don't see why not.

vestal minnow
#

You'd have to pass the system ID every time though, right? Or can you make it const somehow

#

I feel like you kinda lose the point of the custom command if you need to get the system ID it depends on from somewhere else every time

little berry
#

Hmm, yeah.

jovial herald
#

The simplest solution

#[derive(Debug, Default, Resource, Reflect, Serialize, Deserialize)]
#[reflect(Resource)] // <-- forgot this one once again
struct DebugRender(bool);
fn toggle_gizmos(d: Res<DebugRender>, mut store: ResMut<GizmoConfigStore>) {
    if d.is_changed(){
        let (config, _) = store.config_mut::<PhysicsGizmos>();
        config.enabled = d.0;
    }
}
#

kinda redundant to control a resource with a resource but it puts some indirection between my editor/console code and the nitty gritty of what DebugRender actually is

#

still want to look into the commands thing though

abstract forge
vestal minnow
abstract forge
vestal minnow
#

Not yet, but I'll probably start making actual PRs soon and rolling out the stuff I've been working on

#

A lot of it isn't directly related to the new solver but are just general improvements (like structural changes, CCD, joint improvements, etc.)

abstract forge
#

Nice thanks for the quick response.

vestal minnow
#

I'll probably merge the new contact solver last, but I'll try to make a public branch soon-ish

jovial herald
#

Any gotchas for replication? at the moment I'm just replicating pos,rot,linvel and angvel with bevy_replicon

#

And transform (this might produce weird conflict since pos+rot is also replicated but none seen yet)

#

seems to work okay, I just had to make sure my systems for replication ran before all systems for physics

cinder summit
jovial herald
#

oof, sleep is important

#

anyway, I might try not syncing pos or rot, transform should get sync'd

#

one question I have is: given the whole position base dynamics thing, does changing the position/transform get interpreted as a force/velocity or other such thing?

#

because in this case I just want a clean overwrite.

#

interestingly when I pick up my cube in bevy_editor_pls it flies away

jovial herald
#

trying to dig through the gizmo code, what a headache

vestal minnow
#

XPBD basically derives velocity from the change in position caused by constraints

#

(and of course gravity and external forces)

little berry
#

How does joint restituion work in XPBD?

#

I see that a paper's implementation has some bounce.

#

For the joint limits.

#

I guess you'd need to do a separate restitution calculation in addition to the penetration constraint one.

#

Same for linear joint friction?

vestal minnow
#

Joints already have linear and angular damping, which slows down the bodies, but I'm not sure if it's exactly the same as what many engines call joint friction

little berry
#

Yeah, I think damping is just for stability.

#

Friction in this case would represent a prismatic joint's bodies sliding along eachother.

#

If you disable collisions that is.

vestal minnow
#

Godot just calls it damping for prismatic (slider) joints and 6DOF joints, or relaxation for many other joints

#

I guess we'd also want force limits for joints. Should be doable

little berry
#

As in breaking with enough force?

vestal minnow
#

Both that, and just the maximum force a limit can apply, for example

#

not sure how useful that'd be though... Godot's 6DOF joint has it, but most other joints don't seem to

#

PhysX has break thresholds at least

little berry
vestal minnow
#

You can compute the constraint force based on the Lagrange multiplier update. If it exceeds the force threshold, you derive the maximum Lagrange multiplier update from the maximum force and use that instead. Could be wrong, but iirc I got this working some time ago

little berry
#

Oh, I see how that could work.

#

Naming things lagrange and jacobian has done irrepairable damage to the learnability of realtime physics XD

vestal minnow
#

Yep, haha

#

You could even cache the max delta Lagrange if the max force and timestep are constant

#

at the very least, you only need to compute it if the max force is limited, and skip otherwise

#

Also, for the spherical joint limits I'll probably attempt to support something like this

enum SwingLimit {
    Cone {
        swing: f32,
    },
    Pyramid {
        min_swing_y: f32,
        max_swing_y: f32,
        min_swing_z: f32,
        max_swing_z: f32,
    },
}

(API will likely be different)
I haven't found any sources on elliptical cone swing limits, and PhysX seems to be basically the only mainstream engine that has them, so for now I'd only support the circular cone and pyramid

little berry
#

Can't elliptical limits be represented by an ellipsis with semi-axes being total_allowed_angle/2?

vestal minnow
#

Not sure what you mean. What would the angle constraint be like?

little berry
#

My initial thought is that a cone limit can be represented as a circle with r=max_angle

#

An elliptical limit would thus be represented by an ellipsis with a=max_angle_x and b=max_angle_y

vestal minnow
#

What's r here? The radius of the circular cross-section in the cone increases the farther you get from the apex

little berry
#

I'll try to explain when I'm done with this game

little berry
#

Not sure if this is correct though.

blazing sluice
little berry
#

Haven't tested it yet.

little berry
blazing sluice
#

haha yeah I'm making spaz + terraria

little berry
#

Nice, that could be cool.

vestal minnow
vestal minnow
#

Holy crab, I think it might be working basically first try @little berry
a = PI/2 β‰ˆ 1.57 (max swing angle 1)
b = a/2 β‰ˆ 0.785 (max swing angle 2)
c = r (current max swing angle)

#

It's bouncy/explosive when hitting some angles harder, but that might be the restitution issue

little berry
#

Poggers

vestal minnow
#

poggers indeed, thanks for the idea for the limit!

#

I'll need to test more setups and probably visualize the ellipse to make sure it's accurate, but at least this basic setup seems alright

little berry
#

Also need to test limits above 90 degrees

#

And IDK if the ellipse represents anything in the 3d space

#

So visualizing it may not be useful

vestal minnow
#

mostly just looking down from above the cone to see if the body follows the perimeter of the ellipse

little berry
#

Makes sense.

cinder perch
#

Hey so i am not sure if I'm doing anything wrong but I have set the physics plugin to run in the fixedUpdate schedule and when in presenter mode immediate where im getting ~1500fps the physics seems to run very slowly, like a tick every 2-3seconds. while if in presenter mode vsync i get ~120fps and it doesnt happen.

sorry if im stoopid

little berry
#

I think bevy_xpbd default to fixed timestep.

#

You'd have to insert the time resource yourself to tell it to run faster.

cinder perch
#

This is what im currently doing

vestal minnow
#

bevy_xpbd has a custom fixed timestep by default, but in PostUpdate, similar to bevy_rapier (except rapier's timestep isn't fully fixed by default).

Not sure if this is related, but you might want to use Physics::fixed_once_hz when running in FixedUpdate

app.insert_resource(Time::new_with(Physics::fixed_once_hz(64.0)))
#

oh and make sure your FixedUpdate timestep and physics timestep match

little berry
#

Are there improvements for making physics run in fixed update UX in the next release?

#

Seems pretty miserable if you don't know how to do it.

abstract forge
#

Yesterday me and some a friend where playing around with fixed update but it seems to slow down the sim on higher fps monitors.

cinder perch
abstract forge
#

For now we just added this for every system that needs to wait for physics updates

        .add_systems(PostUpdate, camera_controller.after(PhysicsSet::Sync).before(bevy::transform::TransformSystem::TransformPropagate))
#

not ideal but it fixes jitter and lag of the transform

abstract forge
vestal minnow
# little berry Are there improvements for making physics run in fixed update UX in the next rel...

Yeah, it's pretty bad atm. I haven't done improvements yet, but I could at least make it so that you don't need fixed_once_hz and FixedUpdate should work out of the box even with fixed_hz. And I should add better documentation for FixedUpdate usage.

Eventually, we should probably rework the scheduling to just use FixedUpdate by default similar to basically every other game engine. This hasn't been done yet, mostly because of Bevy's FixedUpdate historically having some issues in earlier releases (and it still has some issues afaik). But nowadays I think it should be more viable

little berry
#

Yeah, it was very funny when any load in FU would feedback loop into itself.

#

Should be fine now, from my limited experience.

#

Rapier seemed to run fine in fixed update for me.

vestal minnow
#

Yeah. I would personally leave a rework like this to the release after the next release though (i.e. after the solver rework) to avoid too many breaking changes

little berry
#

Fair.

#

The rebrand seems like a project that stalls your average FOSS project and burns out the author.

#

So getting it done earlier that later is preferable.

vestal minnow
#

Haha yeah, at least I'm done with school until fall so I don't have that to stress about

little berry
#

I've got another style nitpick.
Instead of having a giant idented if block, it's recommended to use a guard clause.

Instead of

if let Some(angle_limit) = self.angle_limit {
  //do many things
}
0.0

do

let Some(angle_limit) = self.angle_limit else {
  return 0.0;
};
//do many things
little berry
#

Wish there was a RA action for it tbh. It can already turn the former into a match block.

serene cloak
#

i am trying to build a kinematic character controller in xpbd and have realized that normals are very imprecise when using shape casts via spacial query's shape_hits function. it gives incorrect values when moving almost orthogonal to colliders, even when they are completely aligned to grid. it isn't by a ton, but it's enough to clip through colliders. i was curious if there were any common pitfalls, or if this is known behavior

serene cloak
#

i can work around this by getting the collider's transform, and using transform.translation.back().xyz() as a substitute, but i'd much rather have xpbd give me the normal it has already calculated

drifting marsh
#

I ran into this issue as well when making a character controller, would be interested to see if it could be fixed

vestal minnow
#

Seems like a Parry issue if it's happening for shape casts. Not sure if there's much I can do (other than trying to fix it in Parry)

serene cloak
#

if that's so, it's definitely upsetting

carmine sluice
#

These sorts of reports are invaluable honestly

vestal minnow
#

that's probably not the issue though if it even happens for axis-aligned objects

#

Perhaps I should rename the data from e.g. point1 and normal1 to local_point1 and local_normal1 to make it more explicit...

serene cloak
serene cloak
vestal minnow
#

yeah the docs do mention it, but most people probably don't really think about it at the start and just expect it to be in world space

#

I've seen others get tripped up by it too so it'd probably be good to rename

#

Or alternatively, just make the data world-space by default. But internally it's still computed in local space, so for cases where the local data is better, there'd be unnecessary conversions

#

Looks like Unity and Godot just return the world-space point and normal. That might ultimately be the most user-friendly option...

One idea could be to keep the local data, but to also store the transform of the entity that was hit. This way, you could directly get the global data via helper methods without having to separately query for the position of the entity that was hit. I think this could be a really flexible and convenient approach

#

For prior art, Unity returns the transform, for example

#

With that approach, this

fn my_system(spatial: SpatialQuery, transforms: Query<&GlobalTransform>) {
    if let Some(hit) = spatial.cast_ray(Vec3::ZERO, Direction3d::X, 100.0, true, default()) {
        if let Some(transform) = transforms.get(hit.entity) {
            println!("{}", transform.rotation() * hit.normal);
        }
    }
}

would be just something like this

fn my_system(spatial: SpatialQuery) {
    if let Some(hit) = spatial.cast_ray(Vec3::ZERO, Direction3d::X, 100.0, true, default()) {
        println!("{}", hit.global_normal());
    }
}
little berry
vestal minnow
# little berry >Looks like Unity and Godot just return the world-space point and normal. That m...

Yeah my idea was this

One idea could be to keep the local data, but to also store the transform of the entity that was hit.
So e.g. ShapeHitData would be roughly like this

pub struct ShapeHitData {
    pub entity: Entity,
    pub time_of_impact: Scalar,
    pub local_point1: Vector,
    pub local_point2: Vector,
    pub local_normal1: Vector,
    pub local_normal2: Vector,
    /// The transform of the entity that was hit.
    /// (ideally this would be a PhysicsIsometry type)
    pub transform: GlobalTransform,
}

impl ShapeHitData {
    pub fn global_point(&self) -> Vector {
        self.transform.transform_point(self.local_point1)
    }
    pub fn global_normal(&self) -> Vector {
        self.transform.rotation * self.local_normal1
    }
    // ...
}
#

Of course storing local data for both colliders uses more memory than just storing the global data though

little berry
#

I suppose that is the best option for default.

#

Memory is cheap.

vestal minnow
#

yeah

little berry
#

Although GlobalTransform doesn't have rotation.

vestal minnow
#

The actual implementation would store the Position and Rotation

little berry
#

Precomputing scale sure is convenient.

vestal minnow
vestal minnow
little berry
#

No, I mean that baking scale into the colliders helps avoid so much headache.

vestal minnow
#

Yeah, currently colliders store both the scaled and unscaled version (Rapier does the same). I'm pretty sure only storing the scaled version would be lossy

#

and have some other issues

#

But perhaps that'd only be an issue if you scale the same collider multiple times, and at that point we could leave it to the user to cache the unscaled shape

little berry
#

I wonder why parry/rapier stores all the shapes on the heap.

#

Seems like it'd only be useful for meshes.

vestal minnow
#

I imagine it'd hurt performance and cause lots of other issues if you stored shapes in multiple different places. So if you store meshes and convex polyhedra on the heap, you should probably store other shapes there too.

#

Or at least in the same component

#

I guess you could use an enum

#

I still need to try how viable colliders as assets is, but it's blocked on Parry's shapes not implementing TypePath

cinder summit
vestal minnow
#

Yeah, to support collider assets we'd need to change every system that queries for colliders to have Assets<MyCollider> in the system signature and query for handles instead. I'm not sure how we could nicely support colliders as both components and assets

#

I guess technically some kind of generic system parameter could work

cinder summit
#

Yea you'd probably want to let the trait specify a system param that should be queried for it

#

Otherwise you'd need spheres as assets in that example which seems a bit silly thonk

vestal minnow
#

Uhh, I think something roughly like this might work

trait ColliderQuery<'w, 's, C: AnyCollider, F: QueryFilter = ()> {
    fn get(&'s self, entity: Entity) -> Option<&'s C>;
}

#[derive(SystemParam)]
pub struct ColliderComponentQuery<'w, 's, C: AnyCollider + Component, F: QueryFilter + 'static = ()>
{
    pub query: Query<'w, 's, &'static C, F>,
}

impl<'w, 's, C: AnyCollider + Component + 'static, F: QueryFilter> ColliderQuery<'w, 's, C, F>
    for ColliderComponentQuery<'w, 's, C, F>
{
    fn get(&'s self, entity: Entity) -> Option<&'s C> {
        self.query.get(entity).ok()
    }
}

#[derive(SystemParam)]
struct ColliderAssetQuery<'w, 's, C: AnyCollider + Asset, F: QueryFilter + 'static> {
    query: Query<'w, 's, &'static Handle<C>, F>,
    colliders: Res<'w, Assets<C>>,
}

impl<'w, 's, C: AnyCollider + Asset, F: QueryFilter> ColliderQuery<'w, 's, C, F>
    for ColliderAssetQuery<'w, 's, C, F>
{
    fn get(&'s self, entity: Entity) -> Option<&'s C> {
        self.query
            .get(entity)
            .ok()
            .and_then(|handle| self.colliders.get(handle))
    }
}

pub trait AnyCollider: Send + Sync + 'static {
    type Query<'w, 's>: ColliderQuery<'w, 's, Self>
    where
        Self: Sized;
    // ...
}
#

And then use in a system

fn init_colliders<C: AnyCollider>(
    mut commands: Commands,
    collider_query: C::Query<'_, '_>,
    // ...
#

The lifetimes are ugly though, and this approach adds an extra query for colliders, since if you need other components, you need to do that in a separate query

#

You could probably add generic QueryData for the query as well, but I'm not sure how I can add the collider component to the same query

#

I guess 0.14's query joins, but idk about the performance of that. Or maybe QueryState has some nicer approach, I haven't used it

cinder summit
#

I think the simpler way would be to just add an associated SystemParam as an extra, which would just have Res<Assets<C>> or some query to fetch them or however else they're stored

#

Since we can just store the handle in Collider, instead of making it Handle<Collider>

#

If it's useful there could even be separate asset pools for different types of colliders

vestal minnow
cinder summit
#

Well you'd just store it on the trait, and since it's a SystemParam you could request Collider::Param (or since rust is probably gonna complain, <Collider as AnyCollider>::Param, then you pass a reference to the value when you do things with AnyCollider)

pub trait AnyCollider: Send + Sync + 'static {
    type Param: SystemParam;
vestal minnow
#

or something else

#

I feel like that'd be a bit strange from an API perspective since it'd be nice to be able to use the methods directly without any World access, and if you used multiple methods in the same system, it'd have to fetch the collider separately each time (for assets at least)

cinder summit
#

Yes or for end user systems:

fn my_system(
    param: Collider::Param,
    query: Query<&Collider>,
) {
    for collider in &query {
        let aabb = collider.aabb(&param, pos, rot);
        // ...
    }
}
#

If we need to call multiple methods at once we could do something like a "fetched collider" maybe ... Not sure if that would actually happen however

vestal minnow
cinder summit
#

Yea, () implements SystemParam so that would be used

vestal minnow
cinder summit
#

Yea that would also work, we could introduce a layer before AnyCollider called FetchableCollider that has the system param and a fetched collider maybe ... But if it's not necessary that could complicate some types ... Maybe Collider has is an enum, and stores small variants directly, only holding a handle for Mesh/Compound/whatever πŸ€”

vestal minnow
# cinder summit Yea that would also work, we could introduce a layer before AnyCollider called `...

Okay this might not be ideal but:

pub trait Fetchable<T> {
    type Param: SystemParam;
    fn fetch(&self, param: Self::Param) -> T;
}

// Enum for *fetching* collider shapes. Doesn't store large shapes
// like trimeshes, polylines, and other compound shapes directly.
pub enum FetchableShape {
    Sphere(Sphere),
    Cuboid(Cuboid),
    // ...
    TriMesh(Handle<TriMesh>),
}

// The actual shape enum storing the data.
pub enum TypedShape {
    Sphere(Sphere),
    Cuboid(Cuboid),
    // ...
    TriMesh(TriMesh),
}

impl AnyCollider for TypedShape {
    // ...
}

pub struct Collider {
    shape: FetchableShape,
}

impl Fetchable<Option<TypedShape>> for Collider {
    type Param = Res<'static, Assets<Handle<TriMesh>>>;

    fn fetch(&self, param: Self::Param) -> Option<TypedShape> {
        match self.shape {
            FetchableShape::Sphere(sphere) => Some(TypedShape::Sphere(sphere)),
            FetchableShape::Cuboid(cuboid) => Some(TypedShape::Cuboid(cuboid)),
            FetchableShape::TriMesh(handle) => Some(TypedShape::TriMesh(param.get(handle).ok()?)),
        }
    }
}
cinder summit
#

Yea that could work ... But we'd need to be certain it's actually necessary the fetch is worth it, since it looks like a pain to write, and you also double up on all the matches on enums

vestal minnow
#

yeah, and of course one issue with TypedShape being an enum is that they are all the size of the largest variant (I think?), in this case TriMesh

#

unless we add dynamic dispatch stuff again / wrap TriMesh in an Arc or something

cinder summit
#

TriMesh would need to be some reference type anyway I'd imagine

#

Wouldn't want to clone trimeshes every time you access them for something

vestal minnow
#

yup

#

One cool part with this fetchable collider approach is that I think we could remove both AsyncCollider and AsyncSceneCollider, and make it possible to just give a Handle<Mesh> directly to a Collider method. A system would go through all of these colliders, build the collider shapes once the meshes are available, add the shapes as assets, and replace the mesh handle with the actual collider shape's handle.

#

This could also potentially work well with asset preprocessing for convex decomp and such

cinder summit
#

It would also become a lot more obvious how to reuse things the right way

#

Colliders are technically backed by a shared value, but it's kind of obscured by the xpbd API so most apps just have many many duplicate instances of the same colliders

vestal minnow
#

Yeah, exactly

#

Definitely worth trying once Parry supports Reflect / I get peck to a usable state

serene cloak
cinder summit
#

How would xpbd respond if I dashed into an object far enough for the normal to point up instead of in the direction I came from? Would it just respond to the normals or would it also consider the involved velocities that caused the collision? πŸ€”

vestal minnow
little berry
#

That kind of super fast movement is for CCD to fix.

#

Which I believe Jondolf is working on for the next release.

vestal minnow
#

Nise has SDF collisions so the built-in CCD wouldn't work out of the box

cinder summit
#

I could always implement CCD tho

vestal minnow
#

yea

little berry
#

CCD sounds a lot more complicated than it is. I was surprised how straightforward the explanation is.

#

"Just project the bounds lol"

vestal minnow
#

I'll probably try to implement a speculative collisions + (optional) time of impact CCD combo, which seems like what almost every engine is going for

cinder summit
little berry
#

Shapecast the object's bounds along its velocity.

vestal minnow
#

substepped CCD would be a bit more complex though (and more expensive)

#

and non-linear toi queries aren't super trivial

#

(Parry handles that for me tho)

little berry
#

I wonder if CCD for multiple constraints is in any way possible in realtime. Like for super fast spinning joints.

#

I've read, I think, Erin writing about improving joint limits by predicting them.

vestal minnow
#

Yup, predictive joint limits are pretty nice. Trivial with impulse-based joints for at least prismatic and revolute joints. It's probably doable with XPBD too but not quite as easily

#

With impulse-based joints I think you literally just slightly modify the bias term

cosmic dagger
#

Hey guys, quick question:
I'd like to enable/disable colliders. Currently I'm doing that by adding and removing Sensors to let the player pass trough.
The problem now is, when the sensors is triggered once, it gets removed from the collider entity and the collider also doesn't "work" anymore afterwards (not showing up in debug, but in the hierarchy it's still on the entity). Is this expected behavior? πŸ€”

vestal minnow
#

bevy_xpbd doesn't remove colliders or sensors from entities at any point

#

If a sensor collides, all it should do is send a collision event

#

What's the position of the entity when this happens?

cosmic dagger
#

Makes sense, thanks I'll check if it's something on my end. But when the sensor would be removed and the collider component is still on it, it would normally collide again right?

vestal minnow
#

I don't think I've tried that myself, but I believe it should work normally, yes

cosmic dagger
#

ok, was an issue on my end πŸ€¦β€β™‚οΈ thanks Jondolf!

vestal minnow
#

Np!

vestal minnow
#

More joint and constraint improvement progress:

  • New 3D types: SwingLimit, TwistLimit, SwingConstraint, and TwistConstraint
    • SwingLimit is an enum with support for circular cones, elliptic cones, and pyramids (pyramid impl WIP)
    • These constraints can be used on their own, but they're also used by SphericalJoint and GenericJoint, increasing code reuse
    • Custom swing and twist axes are supported
    • Much cleaner and better documented code for the limits
  • I'll most likely yeet the Lagrange multiplier properties from constraints. They confuse people and tie the types to XPBD even though the types could technically be reused by other implementations. The multipliers don't need to be stored if we only use substeps and have no separate position iterations (which is already the case).
#

Random thoughts:

  • It's annoying that the joint types store entity IDs. It ties the types to the ECS and means that composing a constraint (like a spherical joint) out of several other constraints (like a ball-socket constraint and a swing-twist constraint) leads to storing duplicate entity IDs. Storing the entities in a separate component would fix this, and it'd also reduce the need for generic systems in cases where the joint itself isn't needed but the "relationship" is.
  • Some engines like PhysX and Jolt define the twist axis (and hinge axis) as the common X axis. I think the Z axis could make more sense to make 2D and 3D consistent. Bepu also defines Z as the twist axis.
  • I will probably move the point-to-point constraint used by e.g. revolute and spherical joints to its own constraint type for convenience and code reuse. Bepu calls it a BallSocket, Godot calls it a PinJoint2D/3D, and Jolt calls it a PointConstraint. Not sure on the ideal name here...
  • Eventually, we might want to reconsider our other joint names as well. Names like "prismatic" and "revolute" seem to come from engineering, but "slider" and "hinge" are also very common and could be more approachable in the context of game development (although "slider" might be a bit overloaded). Not something I'm changing now, but could be worth considering at some point. For now, I've added these as doc aliases.
little berry
vestal minnow
#

I feel like I've tried that and had some weird issues, but I'll try again tomorrow

little berry
#

I think the Z axis could make more sense to make 2D and 3D consistent. Bepu also defines Z as the twist axis.
I do feel Making NegZ the "Forward" of the joint feels right.

#

And as you say helps with 2d

vestal minnow
#

That would mean that the min/max joint limit is applied the wrong way in 3D

#

I think

little berry
#

What do you mean?

#

Bepu calls it a BallSocket, Godot calls it a PinJoint2D/3D, and Jolt calls it a PointConstraint. Not sure on the ideal name here...
Pin sounds the most relevant. Anchor maybe?

#

Names like "prismatic" and "revolute" seem to come from engineering, but "slider" and "hinge" are also very common and could be more approachable
Yeah, learning the former terms was weird.

vestal minnow
# little berry What do you mean?

A positive rotation is counterclockwise in 2D (because mathematicians). The max limit should be hit when rotating in that direction. But if you used -Z for the axis, I'm pretty sure the max limit would instead be hit in the clockwise direction when viewing from Z towards -Z.

vestal minnow
#

At least right now, with the current impl, 2D and 3D seem to be consistent

vestal minnow
little berry
#

But it would be consistent to the user for the joint to "look" towards the bevy default.

#

I think striving for consistency is key for ease of use.

#

(even if I hate negz forward that bevy chose)

vestal minnow
#

If I had a joint motor for a revolute joint with a positive angular velocity, I'd expect it to rotate counterclockwise in the XY plane, because that's how rotations in Bevy normally work and how AngularVelocity works. This wouldn't work if I made the axis face the opposite direction. That would be inconsistent.

little berry
#

I guess we think about joints differently. To me the axis is the "main" part, and the corresponding plane is assumed.

vestal minnow
#

I just don't see why this:

commands.spawn(revolute_joint.with_motor_target(0.3));

would have a different rotation direction than this:

commands.spawn(PbrBundle {
    mesh,
    material,
    transform: Transform::from_rotation(Quat::from_rotation_z(0.3)),
    ..default()
});

and if we just internally made the axis face the opposite direction, it'd break all of my expectations of what the axis is

#

we'd also only invert e.g. min/max limits in 3D, since 2D doesn't need that

#

since in 2D, rotation is about Z, not -Z

little berry
#

Wait, how does this
Transform::from_rotation(Quat::from_rotation_z(0.3))
line work exactly?

#

All my intuition is gone from having negz be forward.

vestal minnow
#

Viewing from Z towards -Z, it's a counterclockwise rotation of 0.3 radians

little berry
#

So bevy accounts for it properly then.

vestal minnow
#

In 2D:

#

And same in 3D.

#

With Z as the rotation axis, the min/max limits, motors, etc. work like you'd probably expect. On bevy_xpbd main, 2D joints even use Vector3::Z, but locally I just have raw angles, which produces the same result.

ionic gale
#

If I were to make a convex_decomposition collider what would be the best way to make a mesh to match it?

cinder summit
#

Isn't the goal usually for convex_decomposition to match your mesh instead? πŸ€”

inner perch
#

hey guys. I'm wanting to impelement a network stack in my game, but I need cross platform determinism. How far away is this? I read in the docs that it's already locally deterministic...

#

I actually want to sync just user inputs, then replay them on the server. Any suggestions would be appreciated.

cinder summit
#

There's a feature flag for cross platform determinism, I'm not sure if it works perfectly however. I've seen some very slight deviations over time in my game, but if you run into those in practice and can easily reproduce them they are probably fairly easy to fix

inner perch
#

Thanks for the reply, and pardon my ignorance. Do you mean a workaround like syncing some properties sometimes, or periodically syncing full state, something like that?

#

Also, I've never quite understood why ints aren't used for physics engines to gain cross platform determinism...are floats just much faster or something?

vestal minnow
# inner perch Also, I've never quite understood why ints aren't used for physics engines to ga...

Fixed-point numbers are just more painful, and might not be able to take advantage of platform intrinsics and vectorization as well, which can make them significantly slower. They don't really even increase the supported numeric range, and fixed-point math isn't supported by almost any math libraries in Rust (like Glam). And fixed-point numbers aren't even needed for cross-platform determinism, as all you need is scalar implementations for specific functions, mostly transcendental trigonometric operations like sin and cos.

inner perch
#

Actually, the better question for me is, if the server is running Linux on x86 or an ARM processor and the clients on Windows, will Bevy_XBPD be deterministic?

#

I assume you mean things like SIMD instructions don't do int.

vestal minnow
# inner perch Interesting, thanks for the great answer. Does that mean cross platform determi...

I'm not sure if bevy_xpbd is fully deterministic even locally at the moment, so the docs might be inaccurate/outdated there; there are some determinism tests and those pass, and generally it seems to produce the same result, but I'm pretty sure I've also noticed tiny inconsistencies in some specific examples. It's a bit hard to debug properly. But once we're 100% locally deterministic, I believe cross-platform determinism should be pretty straightforward

#

(bevy_rapier also has determinism issues despite having an enhanced-determinism feature)

inner perch
vestal minnow
#

Well I'm working on a big solver rework which should be done later this month. I'll test if that is deterministic, and if not, I can try if I can fix it. But I can't make promises, determinism issues are notoriously hard to debug

inner perch
vestal minnow
# little berry >Bepu calls it a BallSocket, Godot calls it a PinJoint2D/3D, and Jolt calls it a...

Hmm, pin could technically have naming conflicts with Rust's Pin, although in practice a name like PinJoint would probably be fine. "Anchor" could get somewhat confused with the local anchor properties on joints. What it does is constrain a point on one body to a point on another body, so PointConstraint would make sense, but it might be slightly less user-friendly, and the joint-like constraints generally have a FooJoint naming scheme

#

It's ultimately a ball joint / ball socket / spherical joint without the angular limits, and I'm not sure if that really exists as its own joint in real life, since ball joints always have a limited range of motion. So I suppose a more abstract PointConstraint name could be fine too

little berry
vestal minnow
little berry
# vestal minnow What axes did you use for this? I haven't found a combination that'd work proper...
    fn apply_limits(
        &mut self,
        body1: &mut RigidBodyQueryItem,
        body2: &mut RigidBodyQueryItem,
        dt: Scalar,
    ) -> Vector3 {
        let z1 = -self.joint_forward1;
        let (x1, y1) = z1.any_orthonormal_pair();

        let z2 = -self.joint_forward2;
        let (x2, y2) = z2.any_orthonormal_pair();

        let mut swing_lagrange_x = self.swing_lagrange_x;
        let x = self.apply_limits_inner(
            body1,
            body2,
            &self.swing_limit_x,
            &mut swing_lagrange_x,
            x1,
            x2,
            -z1,
            -z2,
            dt,
        );
        self.swing_lagrange_x = swing_lagrange_x;
#

And for the other swing and twist its y1, y2, x1, x2, and -z1, -z2, -x1, -x2,

#

Inner fn is just the old twist limit.

#
fn apply_limits_inner(
        &self,
        body1: &mut RigidBodyQueryItem,
        body2: &mut RigidBodyQueryItem,
        limit: &Option<AngleLimit>,
        lagrange: &mut Scalar,
        a1: Vector3,
        a2: Vector3,
        b1: Vector3,
        b2: Vector3,
        dt: Scalar,
    ) -> Scalar {
        if let Some(joint_limit) = limit {
            let basis_rot1 = self.compute_rotation_with_body_basis(*body1.rotation);

            let a1 = basis_rot1.rotate_vec3(a1);
            let a2 = body2.rotation.rotate_vec3(a2);

            let b1 = basis_rot1.rotate_vec3(b1);
            let b2 = body2.rotation.rotate_vec3(b2);
#

With basis.

vestal minnow
#

Alright, thanks. I'll try that

topaz aurora
#

is this crate currently wasm friendly?

#

and if not, can you point me in the direction of a workaround

vestal minnow
#

It should work on wasm

topaz aurora
#

hmm.. maybe it's my browser

vestal minnow
#

What error(s) are you getting?

topaz aurora
#

heh... i think i figured it out.. dumb mistake on my part i think

#

πŸ˜“

cinder summit
ionic gale
cinder summit
#

But you give a mesh to convex decomposition right? πŸ€”

inner perch
last panther
#

@vestal minnow it seems like friction interaction is not quite stable
I'm applying a velocity to a cube at the center of mass, and it's causing rotation due to interaction with the floor
Not too much of an issue but it is kinda annoying

vestal minnow
#

Well if it's touching the floor then isn't it supposed to cause rotation?

last panther
#

no?
I'm changing the velocity in the center of mass
so there should be equal amounts of resistance from all points of the object so it shouldn't rotate

#

also the velocity is directly in a face direction

#

so it isn't something with it going diagonally or something

vestal minnow
#

Is this 3D?

last panther
#

yes

#

sorry I can't get a vid rn some stuff broke

vestal minnow
#

Oh then yeah, it shouldn't rotate about the up-axis. I thought you meant something like friction causing a box to tip over when pushing it with high friction

last panther
#

no

#

also I'm fixing the x and z rotation axes

vestal minnow
#

Yeah, makes sense. The new solver will handle friction slightly differently and solves penetration via impulses so hopefully that'll be more stable

#

I'll test actually

last panther
#

ok

vestal minnow
last panther
#

that does seem quite a bit better

vestal minnow
#

I think 3D friction could be improved further by also accounting for friction along the bitangent

#

and a block solver would help make box stacking a lot more stable, but for 4 contact points it's a lot harder than for 2

#
  • a block solver wouldn't work as well if we want to solve contacts with SIMD eventually
#

(or it'd be more difficult at least)

cinder summit
#

ExternalForce would be the correct thing to use for acceleration right?

vestal minnow
#

That, or just add to velocity

#

I guess ExternalForce gets applied over multiple substeps

cinder summit
#

My current character controller just does a lerp between current and desired velocity based on some "acceleration" value, but this approach doesn't even really make sense, and is stopping me from reasonably implementing skills that push or pull characters, because they can easily overcome that force because the lerp just goes between bigger values πŸ™ƒ

#

Not entirely sure how I'm gonna do the pushing and pulling yet tho, would either need to be a force or some temporary velocity, we don't have a concept for the latter do we? πŸ€”

vestal minnow
#

Like, give an entity some velocity for X seconds?

cinder summit
#

Yea, without the weird edgecases that might happen if you add velocity and remove it later, like bumping into something, friction, etc

vestal minnow
#

Yeah there's no built-in thing for that

cinder summit
#

Would it just need another copy of the system that applies velocity, but using some temporary velocity component?

vestal minnow
#

You could probably just have a TemporaryVelocity with a Timer and the target velocity, and then set LinearVelocity to that every frame, or do the integration manually yeah

#

lmao I forgot we have a separate LinearVelocity and not Velocity for some reason

cinder summit
#

Guess we'll have to call it TemporaryLinearVelocity now thonk

vestal minnow
#

TempLinVel

#

pre-0.1 naming

cinder summit
#

I wonder what the accurate reperesentation of wind would be anyway ... I guess it's kind of like air pushing you, so I guess it applies velocity? πŸ€”

vestal minnow
#

If you think of it as drag, it'd be

F = 1/2 * fluid_density * relative_velocity^2 * C * A

where C is the drag coefficient that depends on the shape, and A is the cross-sectional area

#

For a capsule, the drag coefficient would probably be close to a sphere, which is 0.47 according to Wikipedia

#

Surely character models are all capsules πŸ˜‚

carmine sluice
cinder summit
#

Ah but I guess that would still sort of make it acceleration, the force just stops when the relative velocity is 0 πŸ€”

last panther
#

hmm
Would it be possible to make some way you could create both the mesh and collider with the same function?
It's kinda annoying to do things and realize that Collider::cylinder takes in the opposite argument list than Cylinder::new

cinder summit
#

I thought jondolf made a way to construct colliders from primitives πŸ€”

vestal minnow
#

yeah you can do Collider::from(cylinder) or cylinder.collider()

cinder summit
#

So well integrated, you'd almost think you were involved in the development of primitives πŸ˜‚

vestal minnow
#

It's annoying that the cylinder and capsule radius and length are reversed for primitives vs. the collider constructors πŸ₯²

cinder summit
#

Deprecate them 😈

vestal minnow
#

yeah idk if I should just swap the order to match Bevy, breaking every app using them without producing errors, or deprecate the separate constructors

cinder summit
#

yeet is always the answer

vestal minnow
#

I made that API vote in #off-topic for xpbd 0.4 but didn't deprecate it yet because everyone gave different answers ferris_sob

last panther
#

lol

#

I'd suggest deprecating tbh

#

Cylinder::new.collider() is not much longer than Collider::cylinder

vestal minnow
#

Yeah, maybe I should do that... Probably not for this release though

last panther
#

what is this release?

#

impulse solver?

vestal minnow
#

This release will mostly focus on the new solver, CCD, joint improvements, and other general stability and performance updates, and the one after that will probably focus more on some UX improvements and API, along with fixing up low-hanging fruit and shrinking the number of issues in the repo

last panther
#

ok

vestal minnow
#
  • we need simulation islands and a better broad phase
cinder summit
#

Performance sounds nice πŸ‘€

vestal minnow
#

We'll see if it's better for your case or just my collision-heavy tests lol

#

islands would really help and fix sleeping though... I might try if I can get a basic implementation working soon

cinder summit
vestal minnow
#

you have only like 3 substeps right?

cinder summit
#

At the moment yes, I'd like to go back to 6 tho ferris_sob

#

I also run the schedule bevy_xpbd runs in 4 times (and that should support higher too) per frame πŸ˜‚

vestal minnow
#

Yeah well you hopefully shouldn't be bottlenecked by the narrow phase after the release at least

cinder summit
#

It's not even really bottlenecked on the narrow phase because sphere on SDF is cheap ... Tho even if I hardcode my collisions that system takes a substantial amount of time

#

Next bevy release should also clear up more room due to some optimizations to running systems and removing the fairly high runtime of the First schedule with all the event systems

vestal minnow
#

Hmm do your SDF collisions support a "prediction distance"? Basically, computing closest points even for non-touching objects within some margin

cinder summit
#

To some extent yes, but if I want to push really large prediction distances I might need to change some early out logic a bit

vestal minnow
#

Okay nice, speculative CCD could potentially work out-of-the-box for you then. It's only good for moderately fast-moving objects though and can cause ghost collisions if the prediction distance is too large

#

(I also haven't implemented it just yet lol, should be pretty easy though)

cinder summit
#

"SDF collisions with speculative CCD" ... People would surely think we're making stuff up at this point πŸ˜‚

vestal minnow
#

TGS Soft with XPBD joints and SDF collisions with speculative CCD

blazing sluice
#

As I understand it, the current broadphase is a sweep-and-prune affair where all the colliders are mixed together in the same sweeps. Pairs that could never intersect because they're on different collision layers will still be sorted against one another. Is this correct?

forest charm
#

I could use some help understanding how to create a platform that a dynamic object stands on and moves with. I created a small demo showing what I tried to do and how it didn't work here: https://github.com/jhgarner/platform-repro. The core of it is that I can't figure out how to make the velocity of a Dynamic object follow the position changes of a Static object and I think I'm missing something fundamental about how LinearVelocity interacts with the delta_time. What would be the right way to accomplish what the demo's trying to do?

cinder summit
cinder summit
# forest charm I could use some help understanding how to create a platform that a dynamic obje...

If we're talking moving platforms that a character stands on it usually involves checking what "ground" entity a character is standing on, then applying the difference in velocity it has now vs previous update to the character's velocity ... But if it's for things that need to behave in a more physically accurate way I think some friction and having the platform be a kinematic body might be all that's needed πŸ€”

last panther
vestal minnow
#

sorryyy wrong ping ferris_sob

#

was responding to @last panther

#

But yeah either way I won't deprecate the methods yet, we can see what we should do later

vestal minnow
#

And as for sweep and prune, it uses a sorting algorithm that takes advantage of spatial and temporal coherence, meaning that bodies tend not to move that much relative to each other within individual timesteps, which makes sorting cheap

last panther
vestal minnow
last panther
#

ah ok

last panther
#

@vestal minnow do you have any suggestions for how I'd run two simulations at once?
Or like rewind the physics to a previous state

#

like could I just set the position & transform to the previous value

#

or is there any physics data that would be messed up

vestal minnow
#

setting position and velocity would be the most important ones

#

(and rotation)

last panther
#

Kinda goes without saying
Shit wait the simulation isn't fully determinsitic right

#

any way to make it so?

#

I just need local determinism really

vestal minnow
#

It probably isn't 100% deterministic but it's hard to even tell without very specific setups

last panther
#

the thing is I'll be storing the player movement
and then like running it back later for like up to a minute

vestal minnow
#

there's the enhanced-determinism feature but that only really helps with cross-platform deterministic math atm

last panther
#

don't think I need that

vestal minnow
last panther
#

I guess I could just store the positions of everything every second or so

#

and reset everything then

#

actually that's a good idea regardless

#

..and I'm already doing that

vestal minnow
#

I still need to properly test if the new solver is more deterministic

last panther
#

okay yeah no that isn't a problem

#

@vestal minnow actually remember I gave the friction example?
That may not be deterministic which could be an issue

#

since it seems like things significantly diverge from what they should be

forest charm
# cinder summit If we're talking moving platforms that a character stands on it usually involves...

Thanks. I'm trying to implement the less physically accurate version. My simplified example skips checking the ground entity by just assuming one, and assumes the platform movement is the only thing affecting the velocity so I just set the followers velocity to the derived following velocity. Even with these assumptions though, I still see the behavior shown in the readme (https://github.com/jhgarner/platform-repro).

cinder summit
#

If you use linear velocity to move the platform as a kinematic body you wouldn't have to mess with the delta times at least ... Just copy the velocity over every frame after the platform updates it πŸ€”

last panther
#

Also please rename anglelimits alpha and beta to min and max
this isn't math land

#

is a breaking change though...

vestal minnow
#

I already have that renamed locally

#

and the joint code is a lot better commented and structured

last panther
#

ok

#

would it break things if I don't touch alpha and beta?

vestal minnow
#

Renaming them? No

last panther
#

no I mean more like
have you redone the rest of the joint api or is it just internal changes apart from that

#

basically how much stuff would I have to rewrite

vestal minnow
#

The properties are different but you'd generally just touch the costructors and helper methods and those are the same. Although I will probably mark some of them as #[deprecated] since there's more configurability now

last panther
#

ok

vestal minnow
#

Like spherical joint limits aren't just one swing limit and one twist limits, there are many different types of swing limits

#

And different limits can have their own compliances

#

And many joints support local frames instead of just anchor points

last panther
#

wdym by that?

vestal minnow
#

A rotation + offset instead of just an offset. Like a fixed joint previously locked the rotations to be equal, but now you can set what the relative orientation should be

last panther
#

oh
I thought the fixed joint would have set the rotations to be equal to what they were at time of creation
but whatever

#

that doesn't really make sense when I think about it

vestal minnow
vestal minnow
#

and probably many other engines

last panther
#

by the way, the current SubstepCount documentation has a broken [Time] reference

#

docs.rs kinda is buggy about external crates sometimes though

cinder summit
last panther
#

ok

cinder summit
#

Also I think if you locally build docs it should report anything that is broken, idk if that's in xpbd's CI tho

vestal minnow
#

It runs cargo check and fmt but I'm not sure if that catches broken doc links

#

Hasn't given an error for that Time reference at least

last panther
#

probably want to cargo doc

cinder summit
#

Don't think so ... It might be possible to include it with some flag ... Clippy also has rules for some of the formatting stuff iirc, could possibly see what bevy does for CI, since it does check docs stuff

last panther
#

hm

vestal minnow
#

yeah I should probably change it to build docs too

#

Bevy seems to use

cargo doc --workspace --all-features --no-deps --document-private-items
cosmic dagger
#

EventReader<Collision> is still the regular way to check sensors right? πŸ€”

vestal minnow
forest charm
# cinder summit If you use linear velocity to move the platform as a kinematic body you wouldn't...

I still have to deal with the delta times because the platform's velocity depends on its current position and where it needs to be. Deriving and setting the LinearVelocity on a kinematic platform works well at low frame rates, but is unstable at high rates. I updated the repo with a minimal example and a short clip showing what's happening https://github.com/jhgarner/platform-repro/tree/kinematic. At least now the two objects move together even when the platform runs off lol.

cinder summit
#

I would just make the physics fixed rate if that was the main issue πŸ€”

forest charm
normal anchor
#

Hello πŸ™‚
Is it possible to set the collider location to the GlobalTransform of the entity and not it's normal Transform?

blazing sluice
normal anchor
blazing sluice
#

Nope, totally vanilla. Do you have the full spatial bundle in both parent and child?

normal anchor
#

In the parent I don't have anything, the parent is only used as a container so my Inspector is not full of entities

blazing sluice
#

I believe if you want GlobalTransform to work properly, the whole hierarchy needs both GlobalTransform and Transform

normal anchor
#

Interesting, I'll try adding them

#

Setting the GlobalTransform to the parent now makes the children's GlobalTransform relative to the parent which is what I wanted to avoid and the reason I used GlobalTransform

vestal minnow
#

GlobalTransform is not relative to the parent, Transform is

#

If the root entity has a default Transform, setting the Transforms of its children should be the same as setting their GlobalTransforms

#

And GlobalTransform in general is intended to be read-only anyways

#

(GlobalTransform docs)

normal anchor
#

I see, I will look more into it, thank you πŸ™‚

abstract forge
#

I have noticed that if you place a raycast component onto a game object its origin is whatever it’s pos was the previous frame or is that an artifact of the debug plugin being a frame late?

inner perch
#

Does XPBD use fixed point math? I ask because I've read this:
"Addendum: I am suggesting to keep the size of the world small. With that said, Both OP ans Jibb Smart bring up the point that moving away from the origin floats have less precision. That will have an effect on physics, one that will be seen far earlier than the edge of the world. Fixed point numbers, well, have fixed precision, they will be equally good (or bad, if you prefer) everywhere. Which is good if we want determinism. I also want to mention that the way we usually do physics has the property of amplifying small variations." on this page: https://gamedev.stackexchange.com/questions/174320/how-can-i-perform-a-deterministic-physics-simulation

#

I'm worried my RTS map will be too big and errors will creep in in the physics calculations.

cinder summit
#

I don't think I've ever heard anyone imply determinism needs fixed point math ... Determinism isn't usually about deterministic results despite different coordinates πŸ€”

#

For the most part if you get your units right the size of maps doesn't usually become an issue, tho if you're dealing with things like entire planets or solar systems a floating point origin system or something else like that definitely becomes necessary to keep sane behavior ... I remember someone tried to hook bevy_xpbd up to bevy_space, but I'm not sure how that went πŸ€”

inner perch
#

This SO article is all about writing deterministic physics. He does seem to have some really good pointers. I'm just worried about this idea that float maths gets less precise the larger numbers get.

last panther
#

determinism and fixed point aren't really related
but tbh if your floating point is having issues at whichever distance you're already having something a bit too big
if you really want better precision use the f64 flag

vestal minnow
#

Yeah you can absolutely get cross-platform determinism with floating-point math, you just need to be careful with specific operations that normally would use intrinsics that can give different results on different hardware.

For large worlds, the solution is more-so to either use double-precision (f64) or a floating origin like big_space. In general I'd recommend a floating origin if that works for your use-case. The main challenge would be handling multiple floating origins for multiplayer, but I think even that would be viable if/when we add built-in support for it to the physics engine

inner perch
#

One of his points: "No, doubles won't save you!" πŸ˜„

cinder summit
cinder summit
vestal minnow
inner perch
#

if it travelled in the same distance and direction

last panther
#

yes but you don't need that to be exact

vestal minnow
#

The difference is so miniscule that it shouldn't matter for 99% of applications

last panther
#

^^that

inner perch
#

OK well, that does sound comforting.

cinder summit
#

The errors should be far too small to notice that, the issue comes in when you have physics deviations that can keep piling up. The gif in the article shows something where a tiny difference could potentially add up for it to eventually perform noticably different behavior

vestal minnow
#

For very large worlds errors can be clearer, but then you can use a floating origin

cinder summit
#

But even then that example looks like there's plenty of points that self correct these deviations πŸ€”

inner perch
vestal minnow
cinder summit
vestal minnow
#

In some cases, even rendering can start showing artifacts before physics has clear issues. Like I once did this test very far from the origin, and the physics seems fine-ish

#

while the rendering is... uh... yeah

cinder summit
#

I can't even see if the physics are fine because of the rendering πŸ˜‚