component
Rocks
Faceted rocks with convex-hull colliders (seeded scatter).
⌖ Explore the library in 3D → · View as Markdown
Add it
npx @runek/cli add rocks
Pulls @react-three/rapier@^2.2.0, @runek/core@^0.15.0.
Use it
import { Rocks } from './runek/Rocks'
<Rocks position={[0, 0, 0]} seed={1} />
Or as a node in a world file (worlds as data):
{ "type": "Rocks", "props": { "position": [0, 0, 0], "seed": 1 } }
Props
| Prop | Type | Default | Description |
|---|---|---|---|
position |
[x, y, z] |
[0, 0, 0] |
Position [x, y, z] in units (1 unit = 1 m, Y-up). |
rotation |
[x, y, z] |
[0, 0, 0] |
Euler rotation [x, y, z] in radians. |
count |
number |
6 |
|
spread |
number |
3 |
Cluster radius, in units. |
size |
number |
0.6 |
Mean rock radius, in units. |
hue |
number |
30 |
|
seed |
number |
1 |
Seed for the deterministic variation: same seed, same result. |
Registry manifest
registry manifest /r/components/rocks.json Self-contained JSON: inlined source plus resolved dependencies, exactly whatrunek add rocks fetches.
Browse the whole catalog in the gallery →.
Source (Rocks.tsx)
import { RigidBody } from '@react-three/rapier'
import { rng, useWorld, type Vec3 } from '@runek/core'
import { useMemo } from 'react'
export interface RocksProps {
position?: Vec3
rotation?: Vec3
count?: number
/** Cluster radius, in units. */
spread?: number
/** Mean rock radius, in units. */
size?: number
hue?: number
seed?: number
}
interface Rock {
pos: Vec3
scale: Vec3
rot: Vec3
radius: number
lightness: number
}
export function Rocks({
position = [0, 0, 0],
rotation = [0, 0, 0],
count = 6,
spread = 3,
size = 0.6,
hue = 30,
seed = 1,
}: RocksProps) {
const { unit } = useWorld()
const rocks = useMemo<Rock[]>(() => {
const next = rng(seed)
return Array.from({ length: count }, () => {
const radius = size * unit * (0.6 + next() * 0.8)
return {
radius,
pos: [(next() - 0.5) * spread * unit, radius * 0.3, (next() - 0.5) * spread * unit],
scale: [0.8 + next() * 0.5, 0.6 + next() * 0.5, 0.8 + next() * 0.5],
rot: [next() * Math.PI, next() * Math.PI, next() * Math.PI],
lightness: 30 + next() * 18,
}
})
}, [count, spread, size, seed, unit])
return (
<RigidBody type="fixed" colliders="hull" position={position} rotation={rotation}>
{rocks.map((r) => (
<mesh
key={`rock-${r.pos[0].toFixed(3)}:${r.pos[2].toFixed(3)}`}
castShadow
receiveShadow
position={r.pos}
rotation={r.rot}
scale={r.scale}
>
<icosahedronGeometry args={[r.radius, 0]} />
<meshStandardMaterial
color={`hsl(${hue}, 8%, ${r.lightness}%)`}
flatShading
roughness={1}
/>
</mesh>
))}
</RigidBody>
)
}