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

Event priority is ignored after creating a primitive from a glTF scene #3338

Open
g-rauhoeft opened this issue Aug 19, 2024 · 1 comment
Open

Comments

@g-rauhoeft
Copy link

g-rauhoeft commented Aug 19, 2024

Hello Poimandres! Thank you for this awesome library! :)

I believe I've found a bug within the library concerning the event priority in conjunction with createPortal and glTF scenes. If I create a primitive and assign the gltf.scene as the object and add a click handler with prevent default, then add a portal in front of it containing a different object also with a click handler, the click handler of the object closest to its respective camera will be used instead of the one with the highest priority. This is because the __r3f data is only assigned to the top level of the primitive, not to the children of it's object. If a child is clicked it causes the hits array in the intersect function to be assembled based on the distance instead of the priority, because no priority can be found, because getRootState can't find the __r3f object on the child.

Here's a small reproduction of the issue: https://codesandbox.io/p/sandbox/tender-lederberg-43dr55

If you click on the ViewCube overlaying Suzanne, the click handler of Suzanne is executed, not the one of the ViewCube, which has a higher priority. The ViewCube at the bottom left works as expected, but only because Suzanne isn't behind it.

A user of the library can work around this by defining the filter function on the canvas for events, traversing the intersections upwards until a __r3f object is found and then sorting by priority, but this is inefficient, because intersect also performs a sort.

I can imagine two ways to fix this issue. I'd be happy to provide a pull request if you could let me know which fix is preferred.

  1. Instantiating a primitive traverses the passed object downwards, assigning the __r3f object to each child.
  2. getRootState traverses the object hierarchy upwards if it can't find the __r3f object on its parameter, until it finds an object that has the __r3f object.

Have a great day!

@g-rauhoeft
Copy link
Author

Here's the user space workaround for the bug:

import { Canvas, events, LocalState, RootState } from '@react-three/fiber';

<Canvas
  events={(state) => {
    return {
      ...events(state),
      filter: (intersections) => {
        const getClosestPriority = (o: Object3D): number => {
          if ((o as any)["__r3f"] !== undefined) {
            return ((o as any)["__r3f"] as LocalState).root.getState().events
              .priority;
          } else {
            return o.parent ? getClosestPriority(o.parent) : 0;
          }
        };
        return intersections
          .sort((a, b) => {
            const aPriority = getClosestPriority(a.object);
            const bPriority = getClosestPriority(b.object);
            return bPriority - aPriority;
          });
      },
    };
  }}
>
  {...}
</Canvas>

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

No branches or pull requests

1 participant