Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

intersects_aabb can return NaN which fails the filter predicate when checking for intersections #118

Open
StrikeForceZero opened this issue Jul 13, 2024 · 1 comment · May be fixed by #119

Comments

@StrikeForceZero
Copy link

From investigating aevyrie/bevy_mod_picking#341, I've discovered that:

pub fn intersects_aabb(ray: Ray3d, aabb: &Aabb, model_to_world: &Mat4) -> Option<[f32; 2]> {
// Transform the ray to model space
let world_to_model = model_to_world.inverse();
let ray_dir: Vec3A = world_to_model.transform_vector3(*ray.direction).into();
let ray_origin: Vec3A = world_to_model.transform_point3(ray.origin).into();
// Check if the ray intersects the mesh's AABB. It's useful to work in model space
// because we can do an AABB intersection test, instead of an OBB intersection test.
let t_0: Vec3A = (aabb.min() - ray_origin) / ray_dir;
let t_1: Vec3A = (aabb.max() - ray_origin) / ray_dir;
let t_min: Vec3A = t_0.min(t_1);
let t_max: Vec3A = t_0.max(t_1);
let mut hit_near = t_min.x;
let mut hit_far = t_max.x;
if hit_near > t_max.y || t_min.y > hit_far {
return None;
}
if t_min.y > hit_near {
hit_near = t_min.y;
}
if t_max.y < hit_far {
hit_far = t_max.y;
}
if (hit_near > t_max.z) || (t_min.z > hit_far) {
return None;
}
if t_min.z > hit_near {
hit_near = t_min.z;
}
if t_max.z < hit_far {
hit_far = t_max.z;
}
Some([hit_near, hit_far])
}
}

intersects_aabb returns [NaN, NaN] because of a divide by 0 when the intersection check is right on the edge of a mesh, thus failing the filter predicate far > 0.0 here:

.filter(|[_, far]| *far >= 0.0)

It was not apparent to me what the right approach to prevent intersects_aabb from returning NaN. It's possible I'm being naive, and there's a more obvious fix, however, this diff seems to solve the symptom of that issue. (I have no idea if this introduces other side effects, but all the tests seem to pass?)

Index: src/immediate.rs
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/immediate.rs b/src/immediate.rs
--- a/src/immediate.rs	(revision Staged)
+++ b/src/immediate.rs	(date 1720831909681)
@@ -257,7 +257,7 @@
                 };
                 if should_raycast {
                     if let Some([near, _]) = intersects_aabb(ray, aabb, &transform.compute_matrix())
-                        .filter(|[_, far]| *far >= 0.0)
+                        .filter(|[_, far]| *far >= 0.0 || far.is_nan())
                     {
                         aabb_hits_tx.send((FloatOrd(near), entity)).ok();
                     }
@StrikeForceZero
Copy link
Author

StrikeForceZero commented Jul 13, 2024

snippet to reproduce it in headless

#[cfg(test)]
mod tests {
    use super::*;
    use bevy_math::{Dir3, Mat4, Ray3d, Vec3, Vec4};
    use bevy_render::primitives::Aabb;

    #[test]
    fn test_intersects_aabb_nan() {
        let ray = Ray3d {
            origin: Vec3::new(275.0, 247.0, 1000.0),
            direction: Dir3::new_unchecked(Vec3::new(0.0, 0.0, -1.0)),
        };
        let aabb = Aabb {
            center: Vec3A::new(0.0, 0.0, 0.0),
            half_extents: Vec3A::new(25.0, 25.0, 0.0),
        };
        let model_to_world = Mat4 {
            x_axis: Vec4::new(1.0, 0.0, 0.0, 0.0),
            y_axis: Vec4::new(0.0, 1.0, 0.0, 0.0),
            z_axis: Vec4::new(0.0, 0.0, 1.0, 0.0),
            w_axis: Vec4::new(250.0, 350.0, 1.0, 1.0),
        };

        let result = intersects_aabb(ray, &aabb, &model_to_world);

        assert_eq!(result.map(|[near, far]| [near.is_nan(), far.is_nan()]), Some([true, true]));
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
1 participant