Skip to content

Commit

Permalink
fix: forward events while pointer is captured by using getClosestUV
Browse files Browse the repository at this point in the history
  • Loading branch information
bbohlender committed Sep 3, 2024
1 parent a5c09c0 commit 02b638b
Show file tree
Hide file tree
Showing 4 changed files with 202 additions and 127 deletions.
250 changes: 127 additions & 123 deletions examples/uikit/app.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Canvas, useThree } from '@react-three/fiber'
import { useHover, createXRStore, XR } from '@react-three/xr'
import { useHover, createXRStore, XR, XRLayer } from '@react-three/xr'
import { Environment } from '@react-three/drei'
import { Container, Text, Image, Root, setPreferredColorScheme, Fullscreen } from '@react-three/uikit'
import { Button, Slider } from '@react-three/uikit-default'
Expand Down Expand Up @@ -34,140 +34,144 @@ export function App() {
<SwitchToXRPointerEvents />
<XR store={store}>
<Environment preset="city" />
<group position={[0, 1.5, -0.5]}>
<Root pointerEventsType={{ deny: 'grab' }} pixelSize={0.001}>
<Container width="100%" display="flex" alignItems="center" justifyContent="center">
<XRLayer
dpr={4}
pixelWidth={512}
pixelHeight={512}
pointerEventsType={{ deny: 'grab' }}
position={[0, 1.5, -0.5]}
>
<Fullscreen>
<Container
dark={{ backgroundColor: 'rgb(31,41,55)' }}
flexDirection="column"
height="auto"
width="100%"
backgroundColor="rgb(255,255,255)"
borderRadius={8}
overflow="hidden"
>
<Container
dark={{ backgroundColor: 'rgb(31,41,55)' }}
width={384}
flexDirection="column"
height="auto"
backgroundColor="rgb(255,255,255)"
borderRadius={8}
overflow="hidden"
dark={{ backgroundColor: 'rgb(55,65,81)' }}
display="flex"
justifyContent="space-between"
alignItems="center"
borderTopLeftRadius={8}
borderTopRightRadius={8}
backgroundColor="rgb(243,244,246)"
paddingLeft={16}
paddingRight={16}
paddingTop={8}
paddingBottom={8}
>
<Container
dark={{ backgroundColor: 'rgb(55,65,81)' }}
display="flex"
justifyContent="space-between"
alignItems="center"
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"
>
<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)' }} />
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 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>
<Container 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>
<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 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>
<Slider flexGrow={1} flexShrink={1} flexBasis="0%" />
<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>
<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>
<Container 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 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>
</Container>
</Root>
</group>
</Fullscreen>
</XRLayer>
</XR>
</Canvas>
</>
Expand Down
8 changes: 5 additions & 3 deletions packages/pointer-events/src/forward.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Camera, Object3D, Quaternion, Scene, Vector2, Vector3 } from 'three'
import { Camera, Mesh, Object3D, Scene, Vector2 } from 'three'
import { Pointer, PointerOptions } from './pointer.js'
import { NativeEvent, NativeWheelEvent, PointerEvent } from './event.js'
import { CameraRayIntersector } from './intersections/ray.js'
import { generateUniquePointerId } from './pointer/index.js'
import { IntersectionOptions } from './intersections/index.js'
import { getClosestUV } from './utils.js'

export type ForwardablePointerEvent = { pointerId?: number; pointerType?: string; pointerState?: any } & NativeEvent

Expand Down Expand Up @@ -56,10 +57,11 @@ function portalEventToCoords(e: unknown, target: Vector2): Vector2 {
if (!(e instanceof PointerEvent)) {
return target.set(0, 0)
}
if (e.uv == null) {
if (!(e.object instanceof Mesh)) {
return target.set(0, 0)
}
target.copy(e.uv).multiplyScalar(2).addScalar(-1)
getClosestUV(target, e.point, e.object)
target.multiplyScalar(2).addScalar(-1)
return target
}

Expand Down
1 change: 1 addition & 0 deletions packages/pointer-events/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ export * from './intersections/index.js'
export * from './forward.js'
export * from './pointer/index.js'
export * from './combine.js'
export { getClosestUV } from './utils.js'
70 changes: 69 additions & 1 deletion packages/pointer-events/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Object3D } from 'three'
import { BufferAttribute, Matrix4, Mesh, Object3D, Triangle, Vector2, Vector3 } from 'three'
import { PointerEventsMap } from './event.js'

declare module 'three' {
Expand Down Expand Up @@ -53,3 +53,71 @@ const r3fEventToHandlerMap: Record<keyof PointerEventsMap, string> = {
pointerup: 'onPointerUp',
wheel: 'onWheel',
}

const triangleHelper1 = new Triangle()
const triangleHelper2 = new Triangle()
const aVec2Helper = new Vector2()
const bVec2Helper = new Vector2()
const cVec2Helper = new Vector2()
const pointHelper = new Vector3()
const inverseMatrix = new Matrix4()
const localPointHelper = new Vector3()

export function getClosestUV(target: Vector2, point: Vector3, mesh: Mesh): void {
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)
}
let clostestDistance: number | undefined
loopThroughTriangles(mesh, (i1, i2, i3) => {
mesh.getVertexPosition(i1, triangleHelper1.a)
mesh.getVertexPosition(i2, triangleHelper1.b)
mesh.getVertexPosition(i3, triangleHelper1.c)

const distance = triangleHelper1.closestPointToPoint(localPointHelper, pointHelper).distanceTo(localPointHelper)

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

clostestDistance = distance
triangleHelper2.copy(triangleHelper1)
aVec2Helper.fromBufferAttribute(uv, i1)
bVec2Helper.fromBufferAttribute(uv, i2)
cVec2Helper.fromBufferAttribute(uv, i3)
})

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

triangleHelper2.closestPointToPoint(localPointHelper, pointHelper)

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

function loopThroughTriangles(mesh: Mesh, fn: (i1: number, i2: number, i3: number) => void) {
const drawRange = mesh.geometry.drawRange
if (mesh.geometry.index != null) {
const index = mesh.geometry.index
const start = Math.max(0, drawRange.start)
const end = Math.min(index.count, drawRange.start + drawRange.count)
for (let i = start; i < end; i += 3) {
fn(index.getX(i), index.getX(i + 1), index.getX(i + 2))
}
return
}
const position = mesh.geometry.attributes.position

if (position == null) {
return
}

const start = Math.max(0, drawRange.start)
const end = Math.min(position.count, drawRange.start + drawRange.count)

for (let i = start; i < end; i += 3) {
fn(i, i + 1, i + 2)
}
}

0 comments on commit 02b638b

Please sign in to comment.