Fix ray intersection bug causing picking errors in mutter

Resolves: #1956294
This commit is contained in:
Jonas Ådahl 2021-05-03 14:17:45 +02:00
parent 615b120eec
commit e310446b8c
2 changed files with 165 additions and 1 deletions

View File

@ -0,0 +1,157 @@
From 1ec2948405695745b091587de914db9d9dd51817 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
Date: Tue, 23 Mar 2021 08:24:47 +0100
Subject: [PATCH] ray: Nudge ray direction of axis when intersecting
The formula used to calculate the inverse of the direction vector
doesn't handle the direction vector aligning with an axis. Depending on
the SIMD (or not SIMD) implementation used, a axis aligned vector would
either remain the same, or e.g. end up with NaN components messing up
any future calculations.
Fixing the math to handle this is non-trivial, so for now work around
this by nudging the direction vector slightly off axis so that it has a
better hand of hitting the right box even when the direction is axis
aligned.
Closes: #214
---
src/graphene-ray.c | 26 +++++++++++++++++-
tests/ray.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 92 insertions(+), 1 deletion(-)
diff --git a/src/graphene-ray.c b/src/graphene-ray.c
index 8839b03..66c3393 100644
--- a/src/graphene-ray.c
+++ b/src/graphene-ray.c
@@ -467,6 +467,22 @@ graphene_ray_intersects_sphere (const graphene_ray_t *r,
return graphene_ray_intersect_sphere (r, s, NULL) != GRAPHENE_RAY_INTERSECTION_KIND_NONE;
}
+static inline float
+nudge_off_axis (float v)
+{
+ if (graphene_approx_val (v, 0.f))
+ {
+ if (v < 0.f)
+ return -2 * FLT_EPSILON;
+ else
+ return 2 * FLT_EPSILON;
+ }
+ else
+ {
+ return v;
+ }
+}
+
/**
* graphene_ray_intersect_box:
* @r: a #graphene_ray_t
@@ -485,10 +501,18 @@ graphene_ray_intersect_box (const graphene_ray_t *r,
const graphene_box_t *b,
float *t_out)
{
+ graphene_vec3_t safe_direction;
graphene_vec3_t inv_dir;
+ float d[3];
+
+ graphene_vec3_to_float (&r->direction, d);
+ graphene_vec3_init (&safe_direction,
+ nudge_off_axis (d[0]),
+ nudge_off_axis (d[1]),
+ nudge_off_axis (d[2]));
/* FIXME: Needs a graphene_vec3_reciprocal() */
- inv_dir.value = graphene_simd4f_reciprocal (r->direction.value);
+ inv_dir.value = graphene_simd4f_reciprocal (safe_direction.value);
graphene_vec3_t inv_min;
graphene_vec3_subtract (&(b->min), &r->origin, &inv_min);
diff --git a/tests/ray.c b/tests/ray.c
index 01f372c..afe14cb 100644
--- a/tests/ray.c
+++ b/tests/ray.c
@@ -166,6 +166,72 @@ ray_intersect_triangle (void)
NULL);
}
+static void
+ray_intersects_box (void)
+{
+ graphene_point3d_t min;
+ graphene_point3d_t max;
+ graphene_point3d_t origin;
+ graphene_vec3_t direction;
+ graphene_box_t box;
+ graphene_ray_t ray;
+
+ /* Off center box */
+
+ graphene_point3d_init (&min, 41.843132f, 27.356903f, -50.368336f);
+ graphene_point3d_init (&max, 51.698078f, 29.080172f, -50.368336f);
+ graphene_box_init (&box, &min, &max);
+
+ /* Ray from (0, 0, 0) along an axis *NOT* hitting the above box
+ */
+
+ graphene_point3d_init (&origin, 0, 0, 0);
+ graphene_vec3_init (&direction, 0, 0.495176f, -0.868793f);
+ graphene_ray_init (&ray, &origin, &direction);
+
+ mutest_expect ("intersection kind should be NONE",
+ mutest_int_value (graphene_ray_intersects_box (&ray, &box)),
+ mutest_to_be_false,
+ NULL);
+
+ /* Nudged variant of the above ray */
+
+ graphene_vec3_init (&direction, 0 + 0.0001f, 0.495176f, -0.868793f);
+ graphene_ray_init (&ray, &origin, &direction);
+
+ mutest_expect ("intersection kind should still be NONE",
+ mutest_int_value (graphene_ray_intersects_box (&ray, &box)),
+ mutest_to_be_false,
+ NULL);
+
+ /* Centered box */
+
+ graphene_point3d_init (&min, -5.654480f, 27.356903f, -50.368336f);
+ graphene_point3d_init (&max, 5.654475f, 29.080172f, -50.368336f);
+ graphene_box_init (&box, &min, &max);
+
+ /* Ray from (0, 0, 0) along the axis hitting the above box */
+
+ graphene_point3d_init (&origin, 0, 0, 0);
+ graphene_vec3_init (&direction, 0, 0.495176f, -0.868793f);
+ graphene_ray_init (&ray, &origin, &direction);
+
+ mutest_expect ("intersection kind should be ENTER",
+ mutest_int_value (graphene_ray_intersects_box (&ray, &box)),
+ mutest_to_be_true,
+ NULL);
+
+ /* Nudged variant of the above ray */
+
+ graphene_vec3_init (&direction, 2 * FLT_EPSILON, 0.495176f, -0.868793f);
+ graphene_ray_init (&ray, &origin, &direction);
+
+ mutest_expect ("intersection kind should still be ENTER",
+ mutest_int_value (graphene_ray_intersects_box (&ray, &box)),
+ mutest_to_be_true,
+ NULL);
+}
+
static void
ray_suite (void)
{
@@ -175,6 +241,7 @@ ray_suite (void)
mutest_it ("can compute the closest point to a point on the ray", ray_closest_point_to_point);
mutest_it ("can be transformed", ray_matrix_transform);
mutest_it ("can intersect triangles", ray_intersect_triangle);
+ mutest_it ("can intersect on axis", ray_intersects_box);
}
MUTEST_MAIN (
--
2.31.1

View File

@ -1,12 +1,15 @@
Name: graphene
Version: 1.10.4
Release: 4%{?dist}
Release: 5%{?dist}
Summary: Thin layer of types for graphic libraries
License: MIT
URL: https://github.com/ebassi/graphene
Source: %{url}/releases/download/%{version}/%{name}-%{version}.tar.xz
# Fix ray intersection bug causing picking errors in mutter (#1956294)
Patch1: 0001-ray-Nudge-ray-direction-of-axis-when-intersecting.patch
BuildRequires: gcc
BuildRequires: gobject-introspection-devel >= 1.68.0-3.el9
BuildRequires: gtk-doc
@ -73,6 +76,10 @@ the functionality of the installed %{name} package.
%{_datadir}/installed-tests/
%changelog
* Mon May 03 2021 Jonas Ådahl <jadahl@redhat.com> - 1.10.4-5
- Fix ray intersection bug causing picking errors in mutter
Resolves: #1956294
* Tue Apr 27 2021 Matthias Clasen <mclasen@redhat.com> - 1.10.4-4
- Rebuild with newer gobject-introspection to fix multilib conflict
- Related: rhbz#1915340