component

Pillar

Column with a plinth, a tapered (optionally fluted) shaft, and a capital.

⌖ Explore the library in 3D → · View as Markdown

Tune every prop in the workshop →

Add it

npx @runek/cli add pillar

Pulls @react-three/rapier@^2.2.0, @runek/core@^0.15.0.

Use it

import { Pillar } from './runek/Pillar'

<Pillar position={[0, 0, 0]} />

Or as a node in a world file (worlds as data):

{ "type": "Pillar", "props": { "position": [0, 0, 0] } }

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.
seed number Accepted for the component contract; this component has no seeded variation yet.
height number 3 Height, in units.
radius number 0.28 Shaft radius at the base, in units.
flutes number 0 Vertical flutes around the shaft; 0 = smooth.
color color palette stone Defaults to the world palette’s stone slot.

Registry manifest

registry manifest /r/components/pillar.json Self-contained JSON: inlined source plus resolved dependencies, exactly what runek add pillar fetches.

Browse the whole catalog in the gallery →.

Source (Pillar.tsx)
import { CuboidCollider, RigidBody } from '@react-three/rapier'
import { useWorld, type Vec3 } from '@runek/core'

export interface PillarProps {
  position?: Vec3
  rotation?: Vec3
  /** Accepted for the component contract; this component has no seeded variation yet. */
  seed?: number
  height?: number
  /** Shaft radius at the base, in units. */
  radius?: number
  /** Vertical flutes around the shaft; 0 = smooth. */
  flutes?: number
  /** Defaults to the world palette's `stone` slot. */
  color?: string
}

/** A column: square plinth, tapered shaft, square capital. `flutes` facets the
 *  shaft (a fluted look) when set; 0 leaves it smooth. Builds porches, colonnades,
 *  and ruins. */
export function Pillar({
  position = [0, 0, 0],
  rotation = [0, 0, 0],
  height = 3,
  radius = 0.28,
  flutes = 0,
  color,
}: PillarProps) {
  const { unit, palette } = useWorld()
  const stone = color ?? palette.stone
  const r = radius * unit
  const h = height * unit
  const plinthH = 0.08 * h
  const capH = 0.1 * h
  const shaftH = h - plinthH - capH
  const seg = flutes > 0 ? flutes : 24

  return (
    <RigidBody type="fixed" colliders={false} position={position} rotation={rotation}>
      <CuboidCollider args={[r, h / 2, r]} position={[0, h / 2, 0]} />

      <mesh castShadow receiveShadow position={[0, plinthH / 2, 0]}>
        <boxGeometry args={[r * 2.4, plinthH, r * 2.4]} />
        <meshStandardMaterial color={stone} roughness={0.92} />
      </mesh>

      <mesh castShadow receiveShadow position={[0, plinthH + shaftH / 2, 0]}>
        <cylinderGeometry args={[r * 0.85, r, shaftH, seg]} />
        <meshStandardMaterial color={stone} roughness={0.92} flatShading={flutes > 0} />
      </mesh>

      <mesh castShadow receiveShadow position={[0, h - capH, 0]}>
        <cylinderGeometry args={[r * 1.05, r * 0.85, capH * 0.5, 24]} />
        <meshStandardMaterial color={stone} roughness={0.92} />
      </mesh>
      <mesh castShadow receiveShadow position={[0, h - capH * 0.25, 0]}>
        <boxGeometry args={[r * 2.3, capH * 0.5, r * 2.3]} />
        <meshStandardMaterial color={stone} roughness={0.92} />
      </mesh>
    </RigidBody>
  )
}

Pillar.groundSitting = true