From 9d3009547749b31c3f7c0b175303763ffd3baac4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CIsaac?= Date: Mon, 7 Aug 2023 23:31:33 +1000 Subject: [PATCH] feat: add component --- .storybook/stories/Helper.stories.tsx | 71 +++++++++++++++++++++++++++ README.md | 16 ++++++ src/core/Helper.tsx | 47 ++++++++++++++++++ src/core/index.ts | 1 + 4 files changed, 135 insertions(+) create mode 100644 .storybook/stories/Helper.stories.tsx create mode 100644 src/core/Helper.tsx diff --git a/.storybook/stories/Helper.stories.tsx b/.storybook/stories/Helper.stories.tsx new file mode 100644 index 000000000..a5cc8b5b6 --- /dev/null +++ b/.storybook/stories/Helper.stories.tsx @@ -0,0 +1,71 @@ +import { useFrame } from '@react-three/fiber' +import * as React from 'react' +import * as THREE from 'three' +import { BoxHelper, CameraHelper } from 'three' +import { VertexNormalsHelper } from 'three-stdlib' +import { Helper, PerspectiveCamera, Sphere } from '../../src' +import { Setup } from '../Setup' + +export default { + title: 'Gizmos/Helper', + component: Helper, + decorators: [(storyFn) => {storyFn()}], + args: { + showHelper: true, + }, + argTypes: { + showHelper: { + type: 'boolean', + }, + }, +} + +type StoryProps = { + showHelper: boolean +} + +const Scene: React.FC = ({ showHelper }) => { + const mesh = React.useRef(null!) + + return ( + + + + {showHelper && ( + <> + + + + )} + + ) +} + +export const DefaultStory = (args: StoryProps) => +DefaultStory.storyName = 'Default' + +const CameraScene: React.FC = ({ showHelper }) => { + const camera = React.useRef() + + useFrame(({ clock }) => { + const t = clock.getElapsedTime() + + if (camera.current) { + camera.current.lookAt(0, 0, 0) + + camera.current.position.x = Math.sin(t) * 4 + camera.current.position.z = Math.cos(t) * 4 + } + }) + + return ( + + + + {showHelper && } + + ) +} + +export const CameraStory = (args: StoryProps) => +CameraStory.storyName = 'Camera Helper' diff --git a/README.md b/README.md index b61cb50e8..1ee9df8b9 100644 --- a/README.md +++ b/README.md @@ -935,6 +935,22 @@ useHelper(condition && mesh, BoxHelper, 'red') // you can pass false instead of ``` +#### Helper + +[![](https://img.shields.io/badge/-storybook-%23ff69b4)](https://drei.vercel.app/?path=/story/gizmos-helper--default-story) + +A component for declaratively adding helpers to existing nodes in the scene. It handles removal of the helper on unmount and auto-updates it by default. + +```jsx + + + + + + + +``` + # Shapes #### Plane, Box, Sphere, Circle, Cone, Cylinder, Tube, Torus, TorusKnot, Ring, Tetrahedron, Polyhedron, Icosahedron, Octahedron, Dodecahedron, Extrude, Lathe, Shape diff --git a/src/core/Helper.tsx b/src/core/Helper.tsx new file mode 100644 index 000000000..d1601190d --- /dev/null +++ b/src/core/Helper.tsx @@ -0,0 +1,47 @@ +import { useFrame, useThree } from '@react-three/fiber' +import * as React from 'react' +import { Object3D } from 'three' + +type HelperType = Object3D & { update: () => void; dispose: () => void } +type HelperConstructor = new (...args: any[]) => HelperType +type HelperArgs = T extends [infer _, ...infer R] ? R : never + +export type HelperProps = { + type: T + args?: HelperArgs> +} + +export const Helper = ({ + type: helperConstructor, + args = [] as never, +}: HelperProps) => { + const objectRef = React.useRef(null!) + const helperRef = React.useRef() + + const scene = useThree((state) => state.scene) + + React.useLayoutEffect(() => { + const parent = objectRef.current?.parent + + if (!helperConstructor || !parent) return + + const helper = new helperConstructor(parent, ...args) + + helperRef.current = helper + + // Prevent the helpers from blocking rays + helper.traverse((child) => (child.raycast = () => null)) + + scene.add(helper) + + return () => { + helperRef.current = undefined + scene.remove(helper) + helper.dispose?.() + } + }, [scene, helperConstructor, ...args]) + + useFrame(() => void helperRef.current?.update?.()) + + return +} diff --git a/src/core/index.ts b/src/core/index.ts index 3eaa59981..916db284a 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -57,6 +57,7 @@ export * from './useVideoTexture' export * from './useFont' // Misc +export * from './Helper' export * from './Stats' export * from './StatsGl' export * from './useDepthBuffer'