Skip to content

Commit a1d4fcf

Browse files
committed
wip
1 parent 039798c commit a1d4fcf

File tree

6 files changed

+147
-15
lines changed

6 files changed

+147
-15
lines changed
+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import * as THREE from 'three'
2+
import * as React from 'react'
3+
4+
import { Vector3 } from 'three'
5+
import { Meta, StoryObj } from '@storybook/react'
6+
7+
import { Setup } from '../Setup'
8+
9+
import { Raycaster } from '../../src'
10+
import { ComponentProps, useRef } from 'react'
11+
import { useFrame } from '@react-three/fiber'
12+
13+
export default {
14+
title: 'Abstractions/Raycaster',
15+
component: Raycaster,
16+
decorators: [
17+
(Story) => (
18+
<Setup cameraPosition={new Vector3(0, 0, 10)}>
19+
<Story />
20+
</Setup>
21+
),
22+
],
23+
argTypes: {
24+
near: { control: { type: 'range', min: 0, max: 15 } },
25+
far: { control: { type: 'range', min: 0, max: 15 } },
26+
helper: { control: { type: 'boolean' } },
27+
},
28+
} satisfies Meta<typeof Raycaster>
29+
30+
type Story = StoryObj<typeof Raycaster>
31+
32+
function RaycasterScene(props: React.ComponentProps<typeof Raycaster>) {
33+
return (
34+
<>
35+
<color attach="background" args={['#303030']} />
36+
37+
<Raycaster {...props} />
38+
39+
<Capsule position-x={-2} />
40+
<Capsule />
41+
<Capsule position-x={2} />
42+
</>
43+
)
44+
}
45+
46+
export const RaycasterSt = {
47+
render: (args) => <RaycasterScene {...args} />,
48+
args: {
49+
origin: [-4, 0, 0],
50+
direction: [1, 0, 0],
51+
// near: 1,
52+
// far: 8,
53+
// helper: true,
54+
},
55+
56+
name: 'Default',
57+
} satisfies Story
58+
59+
const Capsule = ({
60+
// layers,
61+
...props
62+
}: ComponentProps<'mesh'>) => {
63+
const meshRef = useRef<THREE.Mesh>(null)
64+
65+
useFrame(({ clock }) => {
66+
if (!meshRef.current) return
67+
meshRef.current.position.y = Math.sin(clock.getElapsedTime() * 0.5 + meshRef.current.position.x)
68+
meshRef.current.rotation.z = Math.sin(clock.getElapsedTime() * 0.5) * Math.PI * 1
69+
})
70+
71+
return (
72+
<mesh ref={meshRef} {...props}>
73+
{/* <Layers layers={layers} /> */}
74+
75+
<capsuleGeometry args={[0.5, 0.5, 4, 32]} />
76+
<meshNormalMaterial side={THREE.DoubleSide} />
77+
</mesh>
78+
)
79+
}

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
},
4747
"dependencies": {
4848
"@babel/runtime": "^7.26.0",
49+
"@gsimone/three-raycaster-helper": "^0.1.0",
4950
"@mediapipe/tasks-vision": "0.10.17",
5051
"@monogrid/gainmap-js": "^3.0.6",
5152
"@react-spring/three": "~9.7.5",

src/core/Helper.tsx

+16-15
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
1+
/* eslint react-hooks/exhaustive-deps: 1 */
12
import * as React from 'react'
23
import { Object3D } from 'three'
34
import { useThree, useFrame } from '@react-three/fiber'
45
import { Falsey } from 'utility-types'
56

6-
type HelperType = Object3D & { update: () => void; dispose: () => void }
7-
type HelperConstructor = new (...args: any[]) => any
8-
type HelperArgs<T> = T extends [infer _, ...infer R] ? R : never
7+
type HelperType = Object3D & { update: () => void; dispose?: () => void }
8+
type HelperConstructor<T = HelperType> = new (...args: any[]) => T
9+
type HelperArgs<T> = T extends [any, ...infer R] ? R : never
910

10-
export function useHelper<T extends HelperConstructor>(
11-
object3D: React.MutableRefObject<Object3D> | Falsey,
12-
helperConstructor: T,
13-
...args: HelperArgs<ConstructorParameters<T>>
11+
export function useHelper<T extends HelperType, H extends HelperConstructor<T>>(
12+
object3D: React.RefObject<ConstructorParameters<H>[0]> | Falsey,
13+
helperConstructor: H,
14+
...args: HelperArgs<ConstructorParameters<H>>
1415
) {
15-
const helper = React.useRef<HelperType>()
16+
const helper = React.useRef<T>()
1617
const scene = useThree((state) => state.scene)
1718
React.useLayoutEffect(() => {
18-
let currentHelper: HelperType = undefined!
19+
let currentHelper: T = undefined!
1920

2021
if (object3D && object3D?.current && helperConstructor) {
2122
helper.current = currentHelper = new (helperConstructor as any)(object3D.current, ...args)
@@ -31,23 +32,23 @@ export function useHelper<T extends HelperConstructor>(
3132
currentHelper.dispose?.()
3233
}
3334
}
34-
}, [scene, helperConstructor, object3D, ...args])
35+
}, [scene, helperConstructor, object3D, args])
3536

3637
useFrame(() => void helper.current?.update?.())
3738
return helper
3839
}
3940

4041
//
4142

42-
export type HelperProps<T extends HelperConstructor> = {
43-
type: T
44-
args?: HelperArgs<ConstructorParameters<T>>
43+
export type HelperProps<H extends HelperConstructor> = {
44+
type: H
45+
args?: HelperArgs<ConstructorParameters<H>>
4546
}
4647

47-
export const Helper = <T extends HelperConstructor>({
48+
export const Helper = <H extends HelperConstructor>({
4849
type: helperConstructor,
4950
args = [] as never,
50-
}: HelperProps<T>) => {
51+
}: HelperProps<H>) => {
5152
const thisRef = React.useRef<Object3D>(null!)
5253
const parentRef = React.useRef<Object3D>(null!)
5354

src/core/Raycaster.tsx

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import * as THREE from 'three'
2+
import * as React from 'react'
3+
import { ComponentProps, forwardRef, useRef, useState } from 'react'
4+
import { useFrame, type Vector3 } from '@react-three/fiber'
5+
import { RaycasterHelper } from '@gsimone/three-raycaster-helper'
6+
7+
import { useHelper } from '..'
8+
9+
type RaycasterProps = Omit<ComponentProps<'raycaster'>, 'args'> & {
10+
origin: Vector3
11+
direction: Vector3
12+
} & {
13+
helper?: boolean
14+
}
15+
16+
function toThreeVec3(v: Vector3) {
17+
return v instanceof THREE.Vector3 ? v : new THREE.Vector3(...(typeof v === 'number' ? [v, v, v] : v))
18+
}
19+
20+
export const Raycaster = forwardRef<THREE.Raycaster, RaycasterProps>(
21+
({ origin: _origin, direction: _direction, near, far, helper, ...props }, fref) => {
22+
const origin = toThreeVec3(_origin)
23+
const direction = toThreeVec3(_direction)
24+
25+
const [r] = useState(() => new THREE.Raycaster(origin, direction))
26+
27+
const raycasterRef = useRef<THREE.Raycaster>(null)
28+
const raycasterHelper = useHelper(helper && raycasterRef, RaycasterHelper)
29+
30+
// Update the hits with intersection results
31+
useFrame(({ scene }) => {
32+
if (!helper) return
33+
if (!raycasterHelper.current || !raycasterRef.current) return
34+
// @ts-ignore
35+
raycasterHelper.current.hits = raycasterRef.current.intersectObjects(scene.children)
36+
})
37+
38+
return <primitive ref={raycasterRef} object={r} {...{ origin, direction, near, far }} {...props} />
39+
}
40+
)

src/core/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export * from './Svg'
2424
export * from './Gltf'
2525
export * from './AsciiRenderer'
2626
export * from './Splat'
27+
export * from './Raycaster'
2728

2829
// Cameras
2930
export * from './OrthographicCamera'

yarn.lock

+10
Original file line numberDiff line numberDiff line change
@@ -2133,6 +2133,15 @@ __metadata:
21332133
languageName: node
21342134
linkType: hard
21352135

2136+
"@gsimone/three-raycaster-helper@npm:^0.1.0":
2137+
version: 0.1.0
2138+
resolution: "@gsimone/three-raycaster-helper@npm:0.1.0"
2139+
peerDependencies:
2140+
three: ^0.139.2
2141+
checksum: 10c0/adefc5b44a449d0ef1540e46454b81b6ff6c267dfb1ad187a1f64da723d6dabc77158958cdd1cb0a63295ecfd9c2e5a4d08577b6821653963e334ef8e4d54b90
2142+
languageName: node
2143+
linkType: hard
2144+
21362145
"@humanfs/core@npm:^0.19.1":
21372146
version: 0.19.1
21382147
resolution: "@humanfs/core@npm:0.19.1"
@@ -2837,6 +2846,7 @@ __metadata:
28372846
"@eslint/compat": "npm:^1.2.3"
28382847
"@eslint/eslintrc": "npm:^3.2.0"
28392848
"@eslint/js": "npm:^9.15.0"
2849+
"@gsimone/three-raycaster-helper": "npm:^0.1.0"
28402850
"@mediapipe/tasks-vision": "npm:0.10.17"
28412851
"@monogrid/gainmap-js": "npm:^3.0.6"
28422852
"@playwright/test": "npm:^1.45.2"

0 commit comments

Comments
 (0)