-
-
Notifications
You must be signed in to change notification settings - Fork 161
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: speeding up intersection traversal for multiple points
- Loading branch information
1 parent
7713473
commit 20117bf
Showing
28 changed files
with
1,524 additions
and
1,145 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
``` | ||
|
||
|
Oops, something went wrong.