diff --git a/.storybook/stories/Raycaster.stories.tsx b/.storybook/stories/Raycaster.stories.tsx new file mode 100644 index 000000000..776f44604 --- /dev/null +++ b/.storybook/stories/Raycaster.stories.tsx @@ -0,0 +1,67 @@ +import * as THREE from 'three' +import * as React from 'react' + +import { Vector3 } from 'three' +import { Meta, StoryObj } from '@storybook/react' + +import { Setup } from '../Setup' + +import { Raycaster } from '../../src' +import { ComponentProps, useRef } from 'react' +import { useFrame } from '@react-three/fiber' + +export default { + title: 'Abstractions/Raycaster', + component: Raycaster, + decorators: [ + (Story) => ( + + + + ), + ], +} satisfies Meta + +type Story = StoryObj + +function RaycasterScene({ origin, direction, ...props }: React.ComponentProps) { + return ( + <> + + + + + + + + + ) +} + +export const RaycasterSt = { + render: (args) => , + + name: 'Default', +} satisfies Story + +const El = ({ + // layers, + ...props +}: ComponentProps<'mesh'>) => { + const $mesh = useRef(null) + + useFrame(({ clock }) => { + if (!$mesh.current) return + $mesh.current.position.y = Math.sin(clock.getElapsedTime() * 0.5 + $mesh.current.position.x) + $mesh.current.rotation.z = Math.sin(clock.getElapsedTime() * 0.5) * Math.PI * 1 + }) + + return ( + + {/* */} + + + + + ) +} diff --git a/package.json b/package.json index aa7360d9e..968925b73 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ }, "dependencies": { "@babel/runtime": "^7.26.0", + "@gsimone/three-raycaster-helper": "^0.1.0", "@mediapipe/tasks-vision": "0.10.17", "@monogrid/gainmap-js": "^3.0.6", "@react-spring/three": "~9.7.5", diff --git a/src/core/Helper.tsx b/src/core/Helper.tsx index 599cb42a3..8bebe3d9f 100644 --- a/src/core/Helper.tsx +++ b/src/core/Helper.tsx @@ -3,19 +3,19 @@ import { Object3D } from 'three' import { useThree, useFrame } from '@react-three/fiber' import { Falsey } from 'utility-types' -type HelperType = Object3D & { update: () => void; dispose: () => void } -type HelperConstructor = new (...args: any[]) => any -type HelperArgs = T extends [infer _, ...infer R] ? R : never - -export function useHelper( - object3D: React.MutableRefObject | Falsey, - helperConstructor: T, - ...args: HelperArgs> +type HelperType = Object3D & { update: () => void; dispose?: () => void } +type HelperConstructor = new (...args: any[]) => T +type HelperArgs = T extends [any, ...infer R] ? R : never + +export function useHelper>( + object3D: React.RefObject[0]> | Falsey, + helperConstructor: H, + ...args: HelperArgs> ) { - const helper = React.useRef() + const helper = React.useRef() const scene = useThree((state) => state.scene) React.useLayoutEffect(() => { - let currentHelper: HelperType = undefined! + let currentHelper: T = undefined! if (object3D && object3D?.current && helperConstructor) { helper.current = currentHelper = new (helperConstructor as any)(object3D.current, ...args) @@ -39,15 +39,15 @@ export function useHelper( // -export type HelperProps = { - type: T - args?: HelperArgs> +export type HelperProps = { + type: H + args?: HelperArgs> } -export const Helper = ({ +export const Helper = ({ type: helperConstructor, args = [] as never, -}: HelperProps) => { +}: HelperProps) => { const thisRef = React.useRef(null!) const parentRef = React.useRef(null!) diff --git a/src/core/Raycaster.tsx b/src/core/Raycaster.tsx new file mode 100644 index 000000000..cc8a7874f --- /dev/null +++ b/src/core/Raycaster.tsx @@ -0,0 +1,32 @@ +import * as THREE from 'three' +import * as React from 'react' +import { ComponentProps, forwardRef, useRef, useState } from 'react' +import { useFrame } from '@react-three/fiber' +import { RaycasterHelper } from '@gsimone/three-raycaster-helper' + +import { useHelper } from '..' + +type RaycasterProps = Omit, 'args'> & { + origin: NonNullable['args']>[0] + direction: NonNullable['args']>[1] +} & { + helper?: boolean +} + +export const Raycaster = forwardRef( + ({ origin, direction, helper, ...props }, fref) => { + const [r] = useState(() => new THREE.Raycaster(origin, direction)) + + const raycasterRef = useRef(null) + + const raycasterHelper = useHelper(!!helper && raycasterRef, RaycasterHelper) + useFrame(({ scene }) => { + if (!raycasterHelper.current || !raycasterRef.current) return + // @ts-ignore + raycasterHelper.current.hits = raycasterRef.current.intersectObjects(scene.children) + }) + + // return + return + } +) diff --git a/src/core/index.ts b/src/core/index.ts index 1842b9299..1b98f7057 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -24,6 +24,7 @@ export * from './Svg' export * from './Gltf' export * from './AsciiRenderer' export * from './Splat' +export * from './Raycaster' // Cameras export * from './OrthographicCamera' diff --git a/yarn.lock b/yarn.lock index a2d08b545..f88214bda 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2133,6 +2133,15 @@ __metadata: languageName: node linkType: hard +"@gsimone/three-raycaster-helper@npm:^0.1.0": + version: 0.1.0 + resolution: "@gsimone/three-raycaster-helper@npm:0.1.0" + peerDependencies: + three: ^0.139.2 + checksum: 10c0/adefc5b44a449d0ef1540e46454b81b6ff6c267dfb1ad187a1f64da723d6dabc77158958cdd1cb0a63295ecfd9c2e5a4d08577b6821653963e334ef8e4d54b90 + languageName: node + linkType: hard + "@humanfs/core@npm:^0.19.1": version: 0.19.1 resolution: "@humanfs/core@npm:0.19.1" @@ -2837,6 +2846,7 @@ __metadata: "@eslint/compat": "npm:^1.2.3" "@eslint/eslintrc": "npm:^3.2.0" "@eslint/js": "npm:^9.15.0" + "@gsimone/three-raycaster-helper": "npm:^0.1.0" "@mediapipe/tasks-vision": "npm:0.10.17" "@monogrid/gainmap-js": "npm:^3.0.6" "@playwright/test": "npm:^1.45.2"