Skip to content

Shape casting returns incorrect normals #193

Description

@sweglord227

Coming from bevy_xpbd, normals are very imprecise when using shape casts via bevy_xpbd's spacial query's shape_hits function. i am creating a kinematic character controller, and 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. after discussing it with the creator of bevy_xpbd and a mod of the bevy discord, i was told it was likely a parry issue and to report it here.

i've also noticed that normals are almost never returned normalized. there tends to be a bit of fluff in one axis or the other, even when colliding head on.

if you need anything else from me, please let me know.

in the image i was clipping into a 10x5x0.1 (bevy coordinates) meter wall aligned to grid. collisions work fine when pushing directly against the wall, but fail when moving into it at a shallow angle.

image

Activity

  1. HeartofPhos commented on Nov 29, 2024

    @HeartofPhos

    I've created some examples where normals behave strangely
    In all cases a target shape is intersecting slightly with the Y+ face of a cuboid, but a minor difference in penetration results in very different normals

    In this case I believe the intended functionality is to always have N2 be [0.0, 1.0, 0.0] (with some floating point imprecision)

    Tested on version 0.17.4

    use parry3d::{
        math::{Isometry, Vector},
        query::{self, ShapeCastOptions},
        shape::{Ball, Capsule, Cuboid, Shape},
    };
    
    fn main() {
        let cases: [fn() -> (Box<dyn Shape>, Isometry<f32>); 4] = [
            // broken
            // N1 [[0.7068505, 0.026921066, 0.7068505]]
            // N2 [[-0.7068505, -0.026921066, -0.7068505]]
            || {
                let g = Capsule::new_y(5.0, 1.0);
                let pos = Isometry::translation(0.0, g.half_height() - 0.01, 0.0);
    
                (Box::new(g), pos)
            },
            // working
            // N1 [[4.7682978e-8, -1.0, 4.7682978e-8]]
            // N2 [[-4.7682978e-8, 1.0, -4.7682978e-8]]
            || {
                let g = Capsule::new_y(5.0, 1.0);
                let pos = Isometry::translation(0.0, g.half_height() - 0.02, 0.0);
    
                (Box::new(g), pos)
            },
            // broken
            // N1 [[-0.70708853, -0.007185684, -0.70708853]]
            // N2 [[0.70708853, 0.007185684, 0.70708853]]
            || {
                let g = Ball::new(1.0);
                let pos = Isometry::translation(0.0, g.radius - 0.2, 0.0);
    
                (Box::new(g), pos)
            },
            // working
            // N1 [[-0.0, -1.0, -0.0]]
            // N2 [[0.0, 1.0, 0.0]]
            || {
                let g = Ball::new(1.0);
                let pos = Isometry::translation(0.0, g.radius - 0.200001, 0.0);
    
                (Box::new(g), pos)
            },
        ];
    
        let (g1, pos1) = cases[0]();
        // velocity not being straight down seems to play a part
        let vel1 = Vector::new(0.1, 1.0, 0.0);
    
        let g2 = Cuboid::new(Vector::new(20.0, 1.0, 20.0));
        let pos2 = Isometry::translation(0.0, -g2.half_extents.y, 0.0);
        let vel2 = Vector::new(0.0, 0.0, 0.0);
    
        let options = ShapeCastOptions {
            compute_impact_geometry_on_penetration: true,
            ..Default::default()
        };
    
        let result = query::cast_shapes(&pos1, &vel1, &*g1, &pos2, &vel2, &g2, options);
    
        if let Some(result) = result.unwrap() {
            println!("// N1 {:?}", result.normal1);
            println!("// N2 {:?}", result.normal2);
            println!("{:?}", result.status);
        }
    }
  2. added 2 commits that reference this issue on Aug 7, 2026
    a9ec9f0
    08c43ea
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-BugSomething isn't workingP-High

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions