Skip to content

Commit

Permalink
feat: speeding up intersection traversal for multiple points
Browse files Browse the repository at this point in the history
  • Loading branch information
bbohlender committed Sep 2, 2024
1 parent 7713473 commit 20117bf
Show file tree
Hide file tree
Showing 28 changed files with 1,524 additions and 1,145 deletions.
1 change: 0 additions & 1 deletion examples/handheld-ar/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ export function App() {
const [bool, setBool] = useState(false)
return (
<>
<button onClick={() => store.enterVR()}>Enter VR</button>
<button onClick={() => store.enterAR()}>Enter AR</button>
<Canvas style={{ width: '100%', flexGrow: 1 }}>
<XR store={store}>
Expand Down
2 changes: 1 addition & 1 deletion examples/rag-doll/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export function App() {
>
Enter VR
</button>
<Canvas dpr={[1, 2]} shadows camera={{ position: [-40, 40, 40], fov: 25, near: 1, far: 100 }}>
<Canvas dpr={[1, 2]} shadows camera={{ position: [-40, 40, 40], fov: 25 }}>
<OrbitControls />
<XR store={store}>
<color attach="background" args={['#171720']} />
Expand Down
2 changes: 1 addition & 1 deletion examples/uikit/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const store = createXRStore({
controller: { teleportPointer: true },
})

setPreferredColorScheme('light')
setPreferredColorScheme('dark')

export function App() {
const [counter, setCounter] = useState(0)
Expand Down
2 changes: 1 addition & 1 deletion examples/uikit/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"dependencies": {
"@pmndrs/pointer-events": "^6.1.0",
"@pmndrs/pointer-events": "workspace:^",
"@react-three/drei": "^9.108.3",
"@react-three/uikit": "^0.4.0",
"@react-three/uikit-default": "^0.4.0",
Expand Down
72 changes: 52 additions & 20 deletions packages/pointer-events/README.md
Original file line number Diff line number Diff line change
@@ -1,48 +1,80 @@
# pointer-events

*framework agnostic pointer-events implementation for three.js*
_framework agnostic pointer-events implementation for three.js_

based on [🎯 Designing Pointer-events for 3D & XR](https://polar.sh/bbohlender/posts/designing-pointer-events-for-3d)

## How to use

```js
import * as THREE from 'three';
import { forwardHtmlEvents } from '@pmndrs/pointer-events';
import * as THREE from 'three'
import { forwardHtmlEvents } from '@pmndrs/pointer-events'

const canvas = document.getElementById("canvas")
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 70, width / height, 0.01, 10 );
camera.position.z = 1;
const canvas = document.getElementById('canvas')
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(70, width / height, 0.01, 10)
camera.position.z = 1
forwardHtmlEvents(canvas, camera, scene)

const width = window.innerWidth, height = window.innerHeight;
const width = window.innerWidth,
height = window.innerHeight

const geometry = new THREE.BoxGeometry( 0.2, 0.2, 0.2 );
const material = new THREE.MeshBasicMaterial({ color: new THREE.Color("red") });
const mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
const geometry = new THREE.BoxGeometry(0.2, 0.2, 0.2)
const material = new THREE.MeshBasicMaterial({ color: new THREE.Color('red') })
const mesh = new THREE.Mesh(geometry, material)
scene.add(mesh)

mesh.addEventListener("pointerover", () => material.color.set("blue"))
mesh.addEventListener("pointerout", () => material.color.set("red"))
mesh.addEventListener('pointerover', () => material.color.set('blue'))
mesh.addEventListener('pointerout', () => material.color.set('red'))

const renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( width, height );
renderer.setAnimationLoop(() => renderer.render( scene, camera ));
const renderer = new THREE.WebGLRenderer({ antialias: true })
renderer.setSize(width, height)
renderer.setAnimationLoop(() => renderer.render(scene, camera))
```

## Filtering

Based on the css `pointer-events` property, the behavior of pointer events can be configured with the values `none`, `listener`, or `auto`.

```js
object.pointerEvents = "none";
object.pointerEvents = 'none'
```

The values `none` and `auto` correspond to the css properties, where `none` means that an object is not directly targetted and `auto` means the object is always targetted for events. The additional value `listener`, which is the default value, expresses that the object is only targetted by events if the object has any listeners. In 3D scenes this default is more reasonable than `auto`, which is the default in the web, because 3D scenes often contain semi-transparent content, such as particles, that should not catch pointer events by default.
The values `none` and `auto` correspond to the css properties, where `none` means that an object is not directly targetted and `auto` means the object is always targetted for events. The additional value `listener`, which is the default value, expresses that the object is only targetted by events if the object has any listeners. In 3D scenes this default is more reasonable than `auto`, which is the default in the web, because 3D scenes often contain semi-transparent content, such as particles, that should not catch pointer events by default.

In addition to the `pointerEvents` property, each 3D object can also filter events based on the `pointerType` with the `pointerEventsType` property. This property defaults to the value `all`, which expresses that pointer events from pointers of all types should be accepted. To filter specific pointer types, such as `screen-mouse`, which represents a normal mouse used through a 2D screen, `pointerEventsType` can be set to `{ allow: "screen-mouse" }` or `{ deny: "screen-touch" }`. `pointerEventsType`'s `allow` and `deny` accept strings and array of strings. In case more custom logic is needed, `pointerEventsType` also accepts a function. In general the pointer types `screen-touch`, `screen-pen`, `ray`, `grab`, and `touch` are used by default. For pointer events that were forwarded through a portal using `forwardObjectEvents`, their `pointerType` is prefixed with `forward-`, while events forwarded from the dom to the scene are prefixed with `screen-`.

## But wait ... there's more

Create your own `Pointer` that can represent a WebXR controller or something else. These `Pointer` can use a normal `Ray` for intersection, or a set of `Lines`, or even a `Sphere`, for grab and touch events.
Create your own `Pointer` that can represent a WebXR controller or something else. These `Pointer` can use a normal `Ray` for intersection, or a set of `Lines`, or even a `Sphere`, for grab and touch events.

## Performance

In some cases multi-modal interactivity requires multiple pointers at the same time. Executing `pointer.move`, such as in the following example, can lead to performance issues because the scene graph will be traversed several times.

```ts
leftGrabPointer.move()
leftTouchPointer.move()
leftRayPointer.move()
rightGrabPointer.move()
rightTouchPointer.move()
rightRayPointer.move()
```

In this case, performance can be improved by combining the pointer using `CombinedPointer`, which will traverse the scene graph once per combined pointer, calculating the intersections for each pointer on each object.

```ts
const leftPointer = new CombinedPointer()
const rightPointer = new CombinedPointer()
leftPointer.register(leftGrabPointer)
leftPointer.register(leftTouchPointer)
leftPointer.register(leftRayPointer)
rightPointer.register(rightGrabPointer)
rightPointer.register(rightTouchPointer)
rightPointer.register(rightRayPointer)

leftPointer.move()
rightPointer.move()
```


Loading

0 comments on commit 20117bf

Please sign in to comment.