Skip to content

Commit

Permalink
compute nearest uv when pointer is captured
Browse files Browse the repository at this point in the history
  • Loading branch information
bbohlender committed Nov 3, 2024
1 parent 1e78c7e commit 4ddbccb
Show file tree
Hide file tree
Showing 11 changed files with 123 additions and 169 deletions.
4 changes: 3 additions & 1 deletion examples/pointer-events/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ getVoidObject(scene).addEventListener('click', () => console.log('click outer'))

const canvas = document.getElementById('root') as HTMLCanvasElement

const { update: updateForwardHtmlEvents } = forwardHtmlEvents(canvas, () => camera, scene)
const { update: updateForwardHtmlEvents } = forwardHtmlEvents(canvas, () => camera, scene, {
intersectEveryFrame: true,
})
const { update: updateForwardObjectEvents } = forwardObjectEvents(plane, () => innerCamera, innerScene)

const renderer = new WebGLRenderer({ antialias: true, canvas })
Expand Down
156 changes: 17 additions & 139 deletions examples/uikit/app.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Canvas } from '@react-three/fiber'
import { createXRStore, noEvents, PointerEvents, XR, XROrigin } from '@react-three/xr'
import { Environment } from '@react-three/drei'
import { Container, Text, Image, Root, setPreferredColorScheme } from '@react-three/uikit'
import { Container, Text, Image, Root, setPreferredColorScheme, Fullscreen } from '@react-three/uikit'
import { Button, Slider } from '@react-three/uikit-default'
import {
ArrowLeftIcon,
Expand Down Expand Up @@ -31,145 +31,23 @@ export function App() {
return (
<>
<button onClick={() => store.enterAR()}>Enter AR</button>
<Canvas events={noEvents} style={{ width: '100%', flexGrow: 1 }}>
<PointerEvents batchEvents={false} />
<XR store={store}>
<XROrigin visible={visible} />
<Environment preset="city" />

<group pointerEventsType={{ deny: 'grab' }} position={[0, 1.5, -0.5]}>
<Root
dark={{ backgroundColor: 'rgb(31,41,55)' }}
flexDirection="column"
pixelSize={0.005}
height="auto"
maxHeight={200}
width="100%"
backgroundColor="rgb(255,255,255)"
borderRadius={8}
overflow="scroll"
>
<Container
dark={{ backgroundColor: 'rgb(55,65,81)' }}
display="flex"
justifyContent="space-between"
alignItems="center"
flexShrink={0}
borderTopLeftRadius={8}
borderTopRightRadius={8}
backgroundColor="rgb(243,244,246)"
paddingLeft={16}
paddingRight={16}
paddingTop={8}
paddingBottom={8}
>
<Text
fontSize={18}
fontWeight={500}
lineHeight={28}
color="rgb(17,24,39)"
dark={{ color: 'rgb(243,244,246)' }}
flexDirection="column"
>
Music Player
</Text>
<Container display="flex" flexDirection="row" gapColumn={8}>
<ExpandIcon color="rgb(17,24,39)" dark={{ color: 'rgb(243,244,246)' }} />
<ConstructionIcon color="rgb(17,24,39)" dark={{ color: 'rgb(243,244,246)' }} />
<MenuIcon color="rgb(17,24,39)" dark={{ color: 'rgb(243,244,246)' }} />
</Container>
</Container>
<Container flexShrink={0} display="flex" flexDirection="column" gapRow={16} padding={16}>
<Container display="flex" alignItems="center" flexDirection="row" gapColumn={16}>
<Image height={64} src="placeholder.svg" width={64} flexDirection="column"></Image>
<Container flexGrow={1} flexShrink={1} flexBasis="0%" flexDirection="column" gapRow={4}>
<Text
fontSize={18}
fontWeight={500}
lineHeight={28}
color="rgb(17,24,39)"
dark={{ color: 'rgb(243,244,246)' }}
flexDirection="column"
>
Blowin' in the Wind
</Text>
<Text
fontSize={14}
lineHeight={20}
color="rgb(107,114,128)"
dark={{ color: 'rgb(156,163,175)' }}
flexDirection="column"
>
Bob Dylan {counter.toString()}
</Text>
</Container>
</Container>
<Slider />
<Container display="flex" alignItems="center" justifyContent="space-between">
<Button size="icon" variant="ghost">
<ArrowLeftIcon color="rgb(17,24,39)" dark={{ color: 'rgb(243,244,246)' }} />
</Button>
<Button onClick={() => setCounter((c) => c + 1)} size="icon" variant="ghost" padding={8}>
<PlayIcon color="rgb(17,24,39)" dark={{ color: 'rgb(243,244,246)' }} />
</Button>
<Button size="icon" variant="ghost">
<ArrowRightIcon color="rgb(17,24,39)" dark={{ color: 'rgb(243,244,246)' }} />
</Button>
</Container>
</Container>
<Container flexShrink={0} padding={16} flexDirection="column">
<Text
fontSize={18}
fontWeight={500}
lineHeight={28}
color="rgb(17,24,39)"
dark={{ color: 'rgb(243,244,246)' }}
marginBottom={8}
flexDirection="column"
>
Playlist
</Text>
<Container flexDirection="column" gapRow={8}>
<Container display="flex" alignItems="center" justifyContent="space-between">
<Text
fontSize={14}
lineHeight={20}
color="rgb(17,24,39)"
dark={{ color: 'rgb(243,244,246)' }}
flexDirection="column"
>
Like a Rolling Stone
</Text>
<PlayIcon color="rgb(17,24,39)" dark={{ color: 'rgb(243,244,246)' }} />
</Container>
<Container display="flex" alignItems="center" justifyContent="space-between">
<Text
fontSize={14}
lineHeight={20}
color="rgb(17,24,39)"
dark={{ color: 'rgb(243,244,246)' }}
flexDirection="column"
>
The Times They Are a-Changin'
</Text>
<PlayIcon color="rgb(17,24,39)" dark={{ color: 'rgb(243,244,246)' }} />
</Container>
<Container display="flex" alignItems="center" justifyContent="space-between">
<Text
fontSize={14}
lineHeight={20}
color="rgb(17,24,39)"
dark={{ color: 'rgb(243,244,246)' }}
flexDirection="column"
>
Subterranean Homesick Blues
</Text>
<PlayIcon color="rgb(17,24,39)" dark={{ color: 'rgb(243,244,246)' }} />
</Container>
</Container>
</Container>
</Root>
</group>
<Canvas events={noEvents}>
<PointerEvents />
<XR store={createXRStore()}>
<Fullscreen pointerEvents="listener">
<Container
width={50}
height={50}
// This will not fire with pointerEvents="listener"
onClick={() => console.log('click container')}
backgroundColor="red"
/>
</Fullscreen>
<mesh onClick={() => console.log('click box')}>
<boxGeometry />
<meshNormalMaterial />
</mesh>
</XR>
</Canvas>
</>
Expand Down
6 changes: 3 additions & 3 deletions examples/uikit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
"dependencies": {
"@pmndrs/pointer-events": "workspace:^",
"@react-three/drei": "^9.108.3",
"@react-three/uikit": "^0.5.3",
"@react-three/uikit-default": "^0.5.3",
"@react-three/uikit-lucide": "^0.5.3",
"@react-three/uikit": "^0.7.0",
"@react-three/uikit-default": "^0.7.0",
"@react-three/uikit-lucide": "^0.7.0",
"@react-three/xr": "workspace:^",
"leva": "^0.9.35"
},
Expand Down
6 changes: 2 additions & 4 deletions packages/pointer-events/src/forward.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,10 @@ function portalEventToCoords(e: unknown, target: Vector2): Vector2 {
if (!(e instanceof PointerEvent)) {
return target.set(0, 0)
}
if (!(e.object instanceof Mesh)) {
if (e.uv == null) {
return target.set(0, 0)
}
getClosestUV(target, e.point, e.object)
target.multiplyScalar(2).addScalar(-1)
return target
return target.copy(e.uv).multiplyScalar(2).addScalar(-1)
}

export function forwardObjectEvents(
Expand Down
1 change: 0 additions & 1 deletion packages/pointer-events/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,3 @@ export * from './intersections/index.js'
export * from './forward.js'
export * from './pointer/index.js'
export * from './combine.js'
export { getClosestUV } from './utils.js'
10 changes: 9 additions & 1 deletion packages/pointer-events/src/intersections/lines.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
Vector3,
Intersection as ThreeIntersection,
Object3D,
Mesh,
Vector2,
} from 'three'
import {
computeIntersectionWorldPlane,
Expand All @@ -18,12 +20,13 @@ import {
import type { PointerCapture } from '../pointer.js'
import { Intersector } from './intersector.js'
import { Intersection, IntersectionOptions } from '../index.js'
import { updateAndCheckWorldTransformation } from '../utils.js'
import { getClosestUV, updateAndCheckWorldTransformation } from '../utils.js'

const invertedMatrixHelper = new Matrix4()
const lineHelper = new Line3()
const planeHelper = new Plane()
const rayHelper = new Ray()
const point2Helper = new Vector2()
const defaultLinePoints = [new Vector3(0, 0, 0), new Vector3(0, 0, 1)]

export class LinesIntersector implements Intersector {
Expand Down Expand Up @@ -74,8 +77,13 @@ export class LinesIntersector implements Intersector {
const point = lineHelper.at(details.distanceOnLine / lineHelper.distance(), new Vector3())
computeIntersectionWorldPlane(planeHelper, intersection, object)
const pointOnFace = rayHelper.intersectPlane(planeHelper, new Vector3()) ?? point
let uv = intersection.uv
if (intersection.object instanceof Mesh && getClosestUV(point2Helper, point, intersection.object)) {
uv = point2Helper.clone()
}
return {
...intersection,
uv,
pointOnFace,
point,
pointerPosition: new Vector3().setFromMatrixPosition(this.fromMatrixWorld),
Expand Down
17 changes: 15 additions & 2 deletions packages/pointer-events/src/intersections/ray.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
Object3D,
Camera,
Vector2,
Mesh,
} from 'three'
import { Intersection, IntersectionOptions } from './index.js'
import { type PointerCapture } from '../pointer.js'
Expand All @@ -18,13 +19,14 @@ import {
voidObjectIntersectionFromRay,
} from './utils.js'
import { Intersector } from './intersector.js'
import { updateAndCheckWorldTransformation } from '../utils.js'
import { getClosestUV, updateAndCheckWorldTransformation } from '../utils.js'

const invertedMatrixHelper = new Matrix4()
const scaleHelper = new Vector3()
const NegZAxis = new Vector3(0, 0, -1)
const directionHelper = new Vector3()
const planeHelper = new Plane()
const point2Helper = new Vector2()

export class RayIntersector implements Intersector {
private readonly raycaster = new Raycaster()
Expand Down Expand Up @@ -72,11 +74,17 @@ export class RayIntersector implements Intersector {
computeIntersectionWorldPlane(planeHelper, intersection, object)
const { ray } = this.raycaster
const pointOnFace = ray.intersectPlane(planeHelper, new Vector3()) ?? intersection.point
const point = ray.direction.clone().multiplyScalar(intersection.distance).add(ray.origin)
let uv = intersection.uv
if (intersection.object instanceof Mesh && getClosestUV(point2Helper, point, intersection.object)) {
uv = point2Helper.clone()
}
return {
...intersection,
uv,
object,
pointOnFace,
point: ray.direction.clone().multiplyScalar(intersection.distance).add(ray.origin),
point,
pointerPosition: ray.origin.clone(),
pointerQuaternion: this.raycasterQuaternion.clone(),
}
Expand Down Expand Up @@ -173,8 +181,13 @@ export class CameraRayIntersector implements Intersector {
}

computeIntersectionWorldPlane(this.viewPlane, intersection, object)
let uv = intersection.uv
if (intersection.object instanceof Mesh && getClosestUV(point2Helper, point, intersection.object)) {
uv = point2Helper.clone()
}
return {
...intersection,
uv,
object,
point,
pointOnFace: point,
Expand Down
10 changes: 9 additions & 1 deletion packages/pointer-events/src/intersections/sphere.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@ import {
Quaternion,
Intersection as ThreeIntersection,
Plane,
Vector2,
} from 'three'
import { computeIntersectionWorldPlane, getDominantIntersectionIndex, pushTimes } from './utils.js'
import type { PointerCapture } from '../pointer.js'
import { Intersector } from './intersector.js'
import { getVoidObject, Intersection, IntersectionOptions } from '../index.js'
import { updateAndCheckWorldTransformation } from '../utils.js'
import { getClosestUV, updateAndCheckWorldTransformation } from '../utils.js'

const scaleHelper = new Vector3()
const point2Helper = new Vector2()

export class SphereIntersector implements Intersector {
private readonly fromPosition = new Vector3()
Expand Down Expand Up @@ -70,10 +72,16 @@ export class SphereIntersector implements Intersector {

const pointOnFace = planeHelper.projectPoint(this.fromPosition, new Vector3())

let uv = intersection.uv
if (intersection.object instanceof Mesh && getClosestUV(point2Helper, point, intersection.object)) {
uv = point2Helper.clone()
}

return {
details: {
type: 'sphere',
},
uv,
distance: intersection.distance,
pointerPosition: this.fromPosition.clone(),
pointerQuaternion: this.fromQuaternion.clone(),
Expand Down
1 change: 1 addition & 0 deletions packages/pointer-events/src/intersections/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export function intersectPointerEventTargets(
const length = pointers.length
if (length === 1) {
if (isAllowed === true || (typeof isAllowed === 'function' && isAllowed(pointers[0]))) {
console.log(object, pointerEvents, pointerEventsOrDefault, pointerEventsType, pointerEventsOrder)
filterAndInteresct(pointers[0], object, pointerEventsOrDefault, pointerEventsType, pointerEventsOrder)
}
} else if (isAllowed === true) {
Expand Down
10 changes: 6 additions & 4 deletions packages/pointer-events/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,11 @@ const pointHelper = new Vector3()
const inverseMatrix = new Matrix4()
const localPointHelper = new Vector3()

export function getClosestUV(target: Vector2, point: Vector3, mesh: Mesh): void {
export function getClosestUV(target: Vector2, point: Vector3, mesh: Mesh): boolean {
localPointHelper.copy(point).applyMatrix4(inverseMatrix.copy(mesh.matrixWorld).invert())
const uv = mesh.geometry.attributes.uv
if (uv == null || !(uv instanceof BufferAttribute)) {
return void target.set(0, 0)
return false
}
let clostestDistance: number | undefined
loopThroughTriangles(mesh, (i1, i2, i3) => {
Expand All @@ -115,7 +115,7 @@ export function getClosestUV(target: Vector2, point: Vector3, mesh: Mesh): void
const distance = triangleHelper1.closestPointToPoint(localPointHelper, pointHelper).distanceTo(localPointHelper)

if (clostestDistance != null && distance >= clostestDistance) {
return void target.set(0, 0)
return
}

clostestDistance = distance
Expand All @@ -126,12 +126,14 @@ export function getClosestUV(target: Vector2, point: Vector3, mesh: Mesh): void
})

if (clostestDistance == null) {
return void target.set(0, 0)
return false
}

triangleHelper2.closestPointToPoint(localPointHelper, pointHelper)

triangleHelper2.getInterpolation(pointHelper, aVec2Helper, bVec2Helper, cVec2Helper, target)

return true
}

function loopThroughTriangles(mesh: Mesh, fn: (i1: number, i2: number, i3: number) => void) {
Expand Down
Loading

0 comments on commit 4ddbccb

Please sign in to comment.