Skip to content

Commit 20117bf

Browse files
committed
feat: speeding up intersection traversal for multiple points
1 parent 7713473 commit 20117bf

28 files changed

+1524
-1145
lines changed

examples/handheld-ar/app.tsx

-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ export function App() {
99
const [bool, setBool] = useState(false)
1010
return (
1111
<>
12-
<button onClick={() => store.enterVR()}>Enter VR</button>
1312
<button onClick={() => store.enterAR()}>Enter AR</button>
1413
<Canvas style={{ width: '100%', flexGrow: 1 }}>
1514
<XR store={store}>

examples/rag-doll/src/App.jsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export function App() {
3636
>
3737
Enter VR
3838
</button>
39-
<Canvas dpr={[1, 2]} shadows camera={{ position: [-40, 40, 40], fov: 25, near: 1, far: 100 }}>
39+
<Canvas dpr={[1, 2]} shadows camera={{ position: [-40, 40, 40], fov: 25 }}>
4040
<OrbitControls />
4141
<XR store={store}>
4242
<color attach="background" args={['#171720']} />

examples/uikit/app.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const store = createXRStore({
2323
controller: { teleportPointer: true },
2424
})
2525

26-
setPreferredColorScheme('light')
26+
setPreferredColorScheme('dark')
2727

2828
export function App() {
2929
const [counter, setCounter] = useState(0)

examples/uikit/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"dependencies": {
3-
"@pmndrs/pointer-events": "^6.1.0",
3+
"@pmndrs/pointer-events": "workspace:^",
44
"@react-three/drei": "^9.108.3",
55
"@react-three/uikit": "^0.4.0",
66
"@react-three/uikit-default": "^0.4.0",

packages/pointer-events/README.md

+52-20
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,80 @@
11
# pointer-events
22

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

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

77
## How to use
88

99
```js
10-
import * as THREE from 'three';
11-
import { forwardHtmlEvents } from '@pmndrs/pointer-events';
10+
import * as THREE from 'three'
11+
import { forwardHtmlEvents } from '@pmndrs/pointer-events'
1212

13-
const canvas = document.getElementById("canvas")
14-
const scene = new THREE.Scene();
15-
const camera = new THREE.PerspectiveCamera( 70, width / height, 0.01, 10 );
16-
camera.position.z = 1;
13+
const canvas = document.getElementById('canvas')
14+
const scene = new THREE.Scene()
15+
const camera = new THREE.PerspectiveCamera(70, width / height, 0.01, 10)
16+
camera.position.z = 1
1717
forwardHtmlEvents(canvas, camera, scene)
1818

19-
const width = window.innerWidth, height = window.innerHeight;
19+
const width = window.innerWidth,
20+
height = window.innerHeight
2021

21-
const geometry = new THREE.BoxGeometry( 0.2, 0.2, 0.2 );
22-
const material = new THREE.MeshBasicMaterial({ color: new THREE.Color("red") });
23-
const mesh = new THREE.Mesh( geometry, material );
24-
scene.add( mesh );
22+
const geometry = new THREE.BoxGeometry(0.2, 0.2, 0.2)
23+
const material = new THREE.MeshBasicMaterial({ color: new THREE.Color('red') })
24+
const mesh = new THREE.Mesh(geometry, material)
25+
scene.add(mesh)
2526

26-
mesh.addEventListener("pointerover", () => material.color.set("blue"))
27-
mesh.addEventListener("pointerout", () => material.color.set("red"))
27+
mesh.addEventListener('pointerover', () => material.color.set('blue'))
28+
mesh.addEventListener('pointerout', () => material.color.set('red'))
2829

29-
const renderer = new THREE.WebGLRenderer( { antialias: true } );
30-
renderer.setSize( width, height );
31-
renderer.setAnimationLoop(() => renderer.render( scene, camera ));
30+
const renderer = new THREE.WebGLRenderer({ antialias: true })
31+
renderer.setSize(width, height)
32+
renderer.setAnimationLoop(() => renderer.render(scene, camera))
3233
```
3334

3435
## Filtering
3536

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

3839
```js
39-
object.pointerEvents = "none";
40+
object.pointerEvents = 'none'
4041
```
4142

42-
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.
43+
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.
4344

4445
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-`.
4546

4647
## But wait ... there's more
4748

48-
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.
49+
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.
50+
51+
## Performance
52+
53+
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.
54+
55+
```ts
56+
leftGrabPointer.move()
57+
leftTouchPointer.move()
58+
leftRayPointer.move()
59+
rightGrabPointer.move()
60+
rightTouchPointer.move()
61+
rightRayPointer.move()
62+
```
63+
64+
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.
65+
66+
```ts
67+
const leftPointer = new CombinedPointer()
68+
const rightPointer = new CombinedPointer()
69+
leftPointer.register(leftGrabPointer)
70+
leftPointer.register(leftTouchPointer)
71+
leftPointer.register(leftRayPointer)
72+
rightPointer.register(rightGrabPointer)
73+
rightPointer.register(rightTouchPointer)
74+
rightPointer.register(rightRayPointer)
75+
76+
leftPointer.move()
77+
rightPointer.move()
78+
```
79+
80+

0 commit comments

Comments
 (0)