diff --git a/.storybook/stories/Grid.stories.tsx b/.storybook/stories/Grid.stories.tsx new file mode 100644 index 000000000..47563592f --- /dev/null +++ b/.storybook/stories/Grid.stories.tsx @@ -0,0 +1,28 @@ +import * as React from 'react' +import { Vector3 } from 'three' + +import { Setup } from '../Setup' +import { Grid, Box } from '../../src' + +export default { + title: 'Gizmos/Grid', + component: Grid, + decorators: [(storyFn) => {storyFn()}], +} + +function UseGridScene() { + return ( + + + + + + + + ) +} + +export const UseGridSceneSt = () => +UseGridSceneSt.story = { + name: 'Default', +} diff --git a/README.md b/README.md index 6384943e3..3bea7de9d 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ The `native` route of the library **does not** export `Html` or `Loader`. The de
  • GizmoHelper
  • PivotControls
  • TransformControls
  • +
  • Grid
  • useHelper
  • Abstractions
  • @@ -647,6 +648,50 @@ If you are using other controls (Orbit, Trackball, etc), you will notice how the ``` +#### Grid + +[![](https://img.shields.io/badge/-storybook-%23ff69b4)](https://drei.vercel.app/?path=/story/gizmos-grid--use-grid-scene-st) + +

    + Demo +

    + +A y-up oriented, shader-based grid implementation. + +```tsx +export type GridMaterialType = { + /** Cell size, default: 0.5 */ + cellSize?: number + /** Cell thickness, default: 0.5 */ + cellThickness?: number + /** Cell color, default: black */ + cellColor?: THREE.ColorRepresentation + /** Section size, default: 1 */ + sectionSize?: number + /** Section thickness, default: 1 */ + sectionThickness?: number + /** Section color, default: #2080ff */ + sectionColor?: THREE.ColorRepresentation + /** Follow camera, default: false */ + followCamera?: boolean + /** Display the grid infinitely, default: false */ + infiniteGrid?: boolean + /** Fade distance, default: 100 */ + fadeDistance?: number + /** Fade strength, default: 1 */ + fadeStrength?: number +} + +export type GridProps = GridMaterialType & { + /** Default plane-geometry arguments */ + args?: ConstructorParameters +} +``` + +```jsx + +``` + #### useHelper [![](https://img.shields.io/badge/-storybook-%23ff69b4)](https://drei.vercel.app/?path=/story/misc-usehelper--default-story) diff --git a/src/core/Grid.tsx b/src/core/Grid.tsx new file mode 100644 index 000000000..9cbea70ae --- /dev/null +++ b/src/core/Grid.tsx @@ -0,0 +1,126 @@ +/** Original grid component https://github.com/threlte/threlte/blob/main/packages/extras/src/lib/components/Grid/Grid.svelte + * By https://github.com/grischaerbe and https://github.com/jerzakm + */ + +import * as React from 'react' +import * as THREE from 'three' +import { extend } from '@react-three/fiber' +import { shaderMaterial } from './shaderMaterial' + +export type GridMaterialType = { + /** Cell size, default: 0.5 */ + cellSize?: number + /** Cell thickness, default: 0.5 */ + cellThickness?: number + /** Cell color, default: black */ + cellColor?: THREE.ColorRepresentation + /** Section size, default: 1 */ + sectionSize?: number + /** Section thickness, default: 1 */ + sectionThickness?: number + /** Section color, default: #2080ff */ + sectionColor?: THREE.ColorRepresentation + /** Follow camera, default: false */ + followCamera?: boolean + /** Display the grid infinitely, default: false */ + infiniteGrid?: boolean + /** Fade distance, default: 100 */ + fadeDistance?: number + /** Fade strength, default: 1 */ + fadeStrength?: number +} + +export type GridProps = GridMaterialType & { + /** Default plane-geometry arguments */ + args?: ConstructorParameters +} + +declare global { + namespace JSX { + interface IntrinsicElements { + gridMaterial: JSX.IntrinsicElements['shaderMaterial'] & GridMaterialType + } + } +} + +const GridMaterial = shaderMaterial( + { + cellSize: 0.5, + sectionSize: 1, + fadeDistance: 100, + fadeStrength: 1, + cellThickness: 0.5, + sectionThickness: 1, + cellColor: new THREE.Color(), + sectionColor: new THREE.Color(), + infiniteGrid: 0, + followCamera: 0, + }, + `varying vec3 worldPosition; + uniform float fadeDistance; + uniform float infiniteGrid; + uniform float followCamera; + void main() { + vec3 pos = position.xzy * (1. + fadeDistance * infiniteGrid); + pos.xz += (cameraPosition.xz * followCamera); + worldPosition = pos; + gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0); + }`, + `varying vec3 worldPosition; + uniform float cellSize; + uniform float sectionSize; + uniform vec3 cellColor; + uniform vec3 sectionColor; + uniform float fadeDistance; + uniform float fadeStrength; + uniform float cellThickness; + uniform float sectionThickness; + uniform float infiniteGrid; + float getGrid(float size, float thickness) { + vec2 r = worldPosition.xz / size; + vec2 grid = abs(fract(r - 0.5) - 0.5) / fwidth(r); + float line = min(grid.x, grid.y) + 1. - thickness; + return 1.0 - min(line, 1.); + } + void main() { + float g1 = getGrid(cellSize, cellThickness); + float g2 = getGrid(sectionSize, sectionThickness); + float d = 1.0 - min(distance(cameraPosition.xz, worldPosition.xz) / fadeDistance, 1.); + vec3 color = mix(cellColor, sectionColor, min(1.,sectionThickness * g2)); + gl_FragColor = vec4(color, (g1 + g2) * pow(d,fadeStrength)); + gl_FragColor.a = mix(0.75 * gl_FragColor.a, gl_FragColor.a, g2); + if (gl_FragColor.a <= 0.0) discard; + #include + #include + }` +) + +export const Grid = React.forwardRef( + ( + { + args, + cellColor = '#000000', + sectionColor = '#2080ff', + cellSize = 0.5, + sectionSize = 1, + followCamera = false, + infiniteGrid = false, + fadeDistance = 100, + fadeStrength = 1, + cellThickness = 0.5, + sectionThickness = 1, + ...props + }: Omit & GridProps, + fRef: React.ForwardedRef + ) => { + extend({ GridMaterial }) + const uniforms1 = { cellSize, sectionSize, cellColor, sectionColor, cellThickness, sectionThickness } + const uniforms2 = { fadeDistance, fadeStrength, infiniteGrid, followCamera } + return ( + + + + + ) + } +) diff --git a/src/core/index.ts b/src/core/index.ts index 9d511b0f1..3f9dd3a27 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -41,6 +41,7 @@ export * from './FirstPersonControls' export * from './GizmoHelper' export * from './GizmoViewcube' export * from './GizmoViewport' +export * from './Grid' // Loaders export * from './useCubeTexture' diff --git a/yarn.lock b/yarn.lock index f1f9a5d76..1053910ab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8684,7 +8684,7 @@ lodash.merge@^4.6.2: lodash.omit@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.omit/-/lodash.omit-4.5.0.tgz#6eb19ae5a1ee1dd9df0b969e66ce0b7fa30b5e60" - integrity sha1-brGa5aHuHdnfC5aeZs4Lf6MLXmA= + integrity sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg== lodash.pick@^4.4.0: version "4.4.0"