Skip to content

Commit

Permalink
feat: add <Helper /> component
Browse files Browse the repository at this point in the history
  • Loading branch information
isaac-mason committed Aug 8, 2023
1 parent dc310ca commit 8b4c8bd
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 0 deletions.
69 changes: 69 additions & 0 deletions .storybook/stories/Helper.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
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) => <Setup>{storyFn()}</Setup>],
args: {
showHelper: true,
},
argTypes: {
showHelper: {
type: 'boolean',
},
},
}

type StoryProps = {
showHelper: boolean
}

const Scene: React.FC<StoryProps> = ({ showHelper }) => {
return (
<Sphere>
<meshBasicMaterial />

{showHelper && (
<>
<Helper type={BoxHelper} args={['royalblue']} />
<Helper type={VertexNormalsHelper} args={[1, 0xff0000]} />
</>
)}
</Sphere>
)
}

export const DefaultStory = (args: StoryProps) => <Scene {...args} />
DefaultStory.storyName = 'Default'

const CameraScene: React.FC<StoryProps> = ({ showHelper }) => {
const camera = React.useRef<THREE.PerspectiveCamera>()

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 (
<PerspectiveCamera makeDefault={false} position={[0, 3, 3]} near={1} far={4} ref={camera}>
<meshBasicMaterial />

{showHelper && <Helper type={CameraHelper} />}
</PerspectiveCamera>
)
}

export const CameraStory = (args: StoryProps) => <CameraScene {...args} />
CameraStory.storyName = 'Camera Helper'
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ The `native` route of the library **does not** export `Html` or `Loader`. The de
<li><a href="#transformcontrols">TransformControls</a></li>
<li><a href="#grid">Grid</a></li>
<li><a href="#usehelper">useHelper</a></li>
<li><a href="#helper">Helper</a></li>
</ul>
<li><a href="#abstractions">Abstractions</a></li>
<ul>
Expand Down Expand Up @@ -935,6 +936,22 @@ useHelper(condition && mesh, BoxHelper, 'red') // you can pass false instead of
<mesh ref={mesh} ... />
```

#### 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
<mesh>
<boxGeometry />
<meshBasicMaterial />
<Helper type={BoxHelper} args={['royalblue']} />
<Helper type={VertexNormalsHelper} args={[1, 0xff0000]} />
</mesh>
```

# Shapes

#### Plane, Box, Sphere, Circle, Cone, Cylinder, Tube, Torus, TorusKnot, Ring, Tetrahedron, Polyhedron, Icosahedron, Octahedron, Dodecahedron, Extrude, Lathe, Shape
Expand Down
47 changes: 47 additions & 0 deletions src/core/Helper.tsx
Original file line number Diff line number Diff line change
@@ -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> = T extends [infer _, ...infer R] ? R : never

export type HelperProps<T extends HelperConstructor> = {
type: T
args?: HelperArgs<ConstructorParameters<T>>
}

export const Helper = <T extends HelperConstructor>({
type: helperConstructor,
args = [] as never,
}: HelperProps<T>) => {
const objectRef = React.useRef<Object3D>(null!)
const helperRef = React.useRef<HelperType>()

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 <object3D ref={objectRef} />
}
1 change: 1 addition & 0 deletions src/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export * from './useVideoTexture'
export * from './useFont'

// Misc
export * from './Helper'
export * from './Stats'
export * from './StatsGl'
export * from './useDepthBuffer'
Expand Down

0 comments on commit 8b4c8bd

Please sign in to comment.