@threlte/extras
<Grid>
A robust grid implementation with multiple tweakable parameters.
<script lang="ts">
import { Canvas, T } from '@threlte/core'
import { Grid } from '@threlte/extras'
import { useTweakpane } from '$lib/useTweakpane'
import Scene from './Scene.svelte'
import { PlaneGeometry } from 'three'
import { createNoise2D } from 'simplex-noise'
const { pane, action, addInput } = useTweakpane({
title: 'Grid',
expanded: false
})
const cellFolder = pane.addFolder({
title: 'Cell settings'
})
const cellSize = addInput({
label: 'Cell size',
value: 1,
params: {
step: 1,
min: 1,
max: 5
},
parent: cellFolder
})
const cellColor = addInput({
label: 'Cell color',
value: `#ccc`,
parent: cellFolder
})
const cellThickness = addInput({
label: 'Cell thickness',
value: 1.4,
params: {
step: 0.1,
min: 1,
max: 10
},
parent: cellFolder
})
const sectionFolder = pane.addFolder({
title: 'Section settings'
})
const sectionSize = addInput({
label: 'Section size',
value: 5,
params: {
step: 1,
min: 1,
max: 50
},
parent: sectionFolder
})
const sectionColor = addInput({
label: 'Section color',
value: `#FF3E00`,
parent: sectionFolder
})
const sectionThickness = addInput({
label: 'Section thickness',
value: 2,
params: {
step: 0.1,
min: 1,
max: 10
},
parent: sectionFolder
})
const generalFolder = pane.addFolder({
title: 'General settings'
})
const gridSize1 = addInput({
label: 'Grid size1',
value: 20,
params: {
step: 1,
min: 1,
max: 100
},
parent: generalFolder
})
const gridSize2 = addInput({
label: 'Grid size2',
value: 20,
params: {
step: 1,
min: 1,
max: 100
},
parent: generalFolder
})
const plane = addInput({
label: 'plane',
value: 'xz',
params: {
options: {
xz: 'xz',
xy: 'xy',
zy: 'zy'
}
},
parent: generalFolder
})
$: planeTyped = $plane as 'xz' | 'xy' | 'zy'
const followCamera = addInput({
label: 'followCamera',
value: false,
parent: generalFolder
})
const infiniteGrid = addInput({
label: 'infiniteGrid',
value: false,
parent: generalFolder
})
const fadeDistance = addInput({
label: 'fadeDistance',
value: 100,
params: {
step: 10,
min: 10,
max: 400
},
parent: generalFolder
})
const backGroundColor = addInput({
label: 'Background color',
value: `#003Eff`,
parent: generalFolder
})
const backgroundOpacity = addInput({
label: 'Background opacity',
value: 0.0,
params: {
step: 0.01,
min: 0,
max: 1
},
parent: generalFolder
})
const fadeStregth = addInput({
label: 'fadeStregth',
value: 1,
params: {
step: 0.1,
min: 0,
max: 20
},
parent: generalFolder
})
const gridGeometry = addInput({
label: 'gridGeometry',
value: 'default',
params: {
options: {
plane: 'default',
terrain: 'Terrain'
}
},
parent: generalFolder
})
const typeFolder = pane.addFolder({
title: 'Types of grid'
})
const gridType: any = addInput({
label: 'gridtype',
value: 'polar',
params: {
options: {
grid: 'grid',
lines: 'lines',
circular: 'circular',
polar: 'polar'
}
},
parent: typeFolder
})
const linesAxis = addInput({
label: 'linesAxis (lines)',
value: 'x',
params: {
options: {
x: 'x',
y: 'y',
z: 'z'
}
},
parent: typeFolder
})
const maxRadius = addInput({
label: 'maxRadius (circular & polar)',
value: 10,
params: {
step: 1,
min: 0,
max: 15
},
parent: typeFolder
})
const cellDividers = addInput({
label: 'cell dividers (polar)',
value: 6,
params: {
step: 1,
min: 0,
max: 18
},
parent: typeFolder
})
const sectionDividers = addInput({
label: 'section dividers (polar)',
value: 2,
params: {
step: 1,
min: 0,
max: 18
},
parent: typeFolder
})
const terrainSize = 30
const geometry = new PlaneGeometry(terrainSize, terrainSize, 100, 100)
const noise = createNoise2D()
const vertices = geometry.getAttribute('position').array
for (let i = 0; i < vertices.length; i += 3) {
const x = vertices[i]
const y = vertices[i + 1]
// @ts-ignore
vertices[i + 2] = noise(x / 5, y / 5) * 1 + noise(x / 40, y / 40) * 2
}
geometry.computeVertexNormals()
</script>
<div use:action />
<div class="relative h-full w-full bg-blue-900">
<Canvas>
{#if $gridGeometry == 'Terrain'}
<Grid
position.y={-2}
plane={planeTyped}
cellColor={$cellColor}
cellSize={$cellSize}
cellThickness={$cellThickness}
sectionColor={$sectionColor}
sectionSize={$sectionSize}
sectionThickness={$sectionThickness}
followCamera={$followCamera}
infiniteGrid={$infiniteGrid}
fadeDistance={$fadeDistance}
fadeStrength={$fadeStregth}
gridSize={[$gridSize1, $gridSize2]}
backgroundColor={$backGroundColor}
backgroundOpacity={$backgroundOpacity}
type={$gridType}
axis={$linesAxis}
maxRadius={$maxRadius}
cellDividers={$cellDividers}
sectionDividers={$sectionDividers}
>
<T is={geometry} />
</Grid>
{:else}
<Grid
plane={planeTyped}
cellColor={$cellColor}
cellSize={$cellSize}
cellThickness={$cellThickness}
sectionColor={$sectionColor}
sectionSize={$sectionSize}
sectionThickness={$sectionThickness}
followCamera={$followCamera}
infiniteGrid={$infiniteGrid}
fadeDistance={$fadeDistance}
fadeStrength={$fadeStregth}
gridSize={[$gridSize1, $gridSize2]}
backgroundColor={$backGroundColor}
backgroundOpacity={$backgroundOpacity}
type={$gridType}
axis={$linesAxis}
maxRadius={$maxRadius}
cellDividers={$cellDividers}
sectionDividers={$sectionDividers}
/>
{/if}
<Scene />
</Canvas>
</div>
<script lang="ts">
import { T } from '@threlte/core'
import { OrbitControls } from '@threlte/extras'
import { BoxGeometry } from 'three'
</script>
<T.PerspectiveCamera
makeDefault
position={[15, 15, 15]}
fov={36}
target={[0, 0, 0]}
>
<OrbitControls />
</T.PerspectiveCamera>
<!-- Make a box in every second cell to show aligment -->
{#each { length: 10 } as _h, x}
{#each { length: 10 } as _v, y}
{#if x % 3 == 0 && y % 3 == 0}
<T.Group position={[x - 4.5, 0.5, y - 4.5]}>
<T.Mesh>
<T.BoxGeometry />
<T.MeshBasicMaterial
args={[
{
color: '#ffffff',
opacity: 0.9,
transparent: true
}
]}
/>
</T.Mesh>
<T.LineSegments>
<T.EdgesGeometry args={[new BoxGeometry()]} />
<T.MeshBasicMaterial
args={[
{
color: 0x000000
}
]}
/>
</T.LineSegments>
</T.Group>
{/if}
{/each}
{/each}
Usage
This component provides sensible defaults. You can initialize the default grid with just <Grid>
. ref
passes a reference from the <T.Mesh/>
the grid is constructed on.
Grid types
The grid type can be selected by setting the type
parameter. The available grid types are:
grid
: represents a standard box grid. It does not require any additional properties. (default)lines
: grid consisting of lines that align along a single world axis. You specify this axis by providing eitherx
,y
orz
to theaxis
property.circular
: grid formed of concentric circles. It includes amaxRadius
property that sets the maximum growth extent for the grid. A value of0
removes this limit, allowing the grid to occupy the entire geometry, even if it results in incomplete circles.polar
: similar to the circular type, but it also features lines that subdivide the concentric circles. It too has amaxRadius
property. Additionally, it has two properties for specifying dividers:cellDivider
andsectionDivider
. These determine how many lines will segment the circle into various sectors. For example, 2 lines result in 4 segments at 90° each, while 6 lines create 12 sectors at 30° apiece.
Grid | Lines | Circular | Polar |
---|---|---|---|
Cells and Sections
Grid is split into cells and sections. Cell is meant to represent the smallest units on your grid, whereas
section is a group of cells. You can adjust the size of the grid by changing the cellSize
and sectionSize
parameters. Size is in Three world units, so for example a mesh with BoxGeometry(1,1,1)
will fit perfectly into
a size 1 cell. By default a cell is 1 unit and a section 10, which means that a grid of 10x10 cells will be
outlined with a section line.
Lines
You can adjust the color and thickness of cell and section lines with cellColor
, cellThickness
, sectionColor
, sectionThickness
.
Grid size and fading
The <Grid>
component is a THREE.Mesh
with a PlaneGeometry
attached to it. The gridSize
parameter defines the size of the PlaneGeometry
.
You can extend the grid into infinity if you set the infiniteGrid
parameter to true
.
Changing fadeDistance
sets how far from the camera position the grid begins to fade by having its alpha reduced. fadeStrength
determines how fast it happens (exponent). fadeStrength = 0
means that there is no fading (not recommended for large grids).
Custom geometry
You have the option to insert your own custom geometry into the <Grid/>
slot. The preceding example demonstrates this by showcasing a preview of a terrain-like geometry generated using Perlin noise.
<Grid>
<T.BoxGeometry />
</Grid>
Follow camera
Setting followCamera
to true applies a transform that moves the grid to the camera’s position on the chosen plane
.