# GlyphOrbitControls

glyphcss ships three pointer-based control modes. All must be placed inside a
`<GlyphScene>` (React / Vue) or receive a scene handle (vanilla). The camera
component wraps the scene.

| Control | Interaction | When to use |
|---|---|---|
| `GlyphOrbitControls` | Left-drag orbits; wheel zooms | General-purpose — object inspection |
| `GlyphMapControls` | Left-drag pans; right-drag / Shift+left orbits; wheel zooms | Top-down maps, floor plans |
| `GlyphFirstPersonControls` | Pointer-lock look; WASD move, jump, crouch | Walk-through scenes |

## Orbit controls

`GlyphOrbitControls` mutates the camera-state object on drag; the rasterizer
reads it and writes one string per pointermove. With `interactiveDownscale` set,
the scene renders at reduced resolution for the duration of the gesture and
restores full detail on release.

### Props

| Prop | Type | Default | Description |
|---|---|---|---|
| `drag` | `boolean` | `true` | Enable click-and-drag rotation |
| `wheel` | `boolean` | `true` | Enable scroll-to-zoom |
| `invert` | `boolean \| number` | `false` | Invert drag direction |
| `pitchRange` | `[number, number] \| null` | `[-90, 90]` | Turntable-mode pitch clamp in degrees. `null` for globe-style unrestricted tumbling (views past either pole; the up-vector never rolls) |
| `mode` | `"turntable" \| "trackball"` | `"turntable"` | `"trackball"` rotates about the screen axis perpendicular to the drag, reaching any orientation including roll (two-finger twist rolls) |
| `animate` | `false \| { speed?, axis?, pauseOnInteraction? }` | `false` | Auto-rotation config. `axis` is `"x"` or `"y"` |

```tsx
import { GlyphCamera, GlyphScene, GlyphMesh, GlyphOrbitControls } from "@glyphcss/react";
import { icosahedronPolygons } from "@glyphcss/core";

const icosa = icosahedronPolygons({ center: [0, 0, 0], size: 1, color: "#44ffcc" });

export function App() {
  return (
    <GlyphCamera rotX={25}>
      <GlyphScene mode="solid">
        <GlyphOrbitControls drag wheel animate={{ axis: "y", speed: 0.5 }} />
        <GlyphMesh polygons={icosa} />
      </GlyphScene>
    </GlyphCamera>
  );
}
```

  

```vue
<template>
  <GlyphCamera :rot-x="25">
    <GlyphScene mode="solid">
      <GlyphOrbitControls drag wheel :animate="{ axis: 'y', speed: 0.5 }" />
      <GlyphMesh :polygons="icosa" />
    </GlyphScene>
  </GlyphCamera>
</template>

<script setup lang="ts">
import { GlyphCamera, GlyphScene, GlyphMesh, GlyphOrbitControls } from "@glyphcss/vue";
import { icosahedronPolygons } from "@glyphcss/core";

const icosa = icosahedronPolygons({ center: [0, 0, 0], size: 1, color: "#44ffcc" });
</script>
```

  

```ts
import { createGlyphCamera, createGlyphScene, createGlyphOrbitControls } from "glyphcss";
import { icosahedronPolygons } from "@glyphcss/core";

const host = document.querySelector<HTMLElement>("#scene")!;
const camera = createGlyphCamera({ rotX: 25 });
const scene = createGlyphScene(host, { camera, mode: "solid" });
scene.add(icosahedronPolygons({ center: [0, 0, 0], size: 1, color: "#44ffcc" }));

const controls = createGlyphOrbitControls(scene, {
  drag: true,
  wheel: true,
  animate: { axis: "y", speed: 0.5 },
});

// Cleanup:
// controls.destroy();
```

## Map controls

Left-drag pans the camera target. Right-drag (or Shift + left-drag) orbits. Wheel
zooms. Best for top-down maps or floor-plan scenes.

### Props

| Prop | Type | Default | Description |
|---|---|---|---|
| `drag` | `boolean` | `true` | Enable pointer-drag pan |
| `wheel` | `boolean` | `true` | Enable scroll-to-zoom |
| `invert` | `boolean \| number` | `false` | Invert drag direction |
| `animate` | `false \| { speed?, axis?, pauseOnInteraction? }` | `false` | Auto-rotation. `axis` is `"x"` or `"y"` |

```tsx
import { GlyphCamera, GlyphScene, GlyphMesh, GlyphMapControls } from "@glyphcss/react";
import { cubePolygons } from "@glyphcss/core";

const cube = cubePolygons({ center: [0, 0, 0], size: 1, color: "#4488ff" });

export function MapDemo() {
  return (
    <GlyphCamera rotX={35}>
      <GlyphScene mode="solid" cols={120} rows={36}>
        <GlyphMapControls drag wheel />
        <GlyphMesh polygons={cube} />
      </GlyphScene>
    </GlyphCamera>
  );
}
```

  

```vue
<template>
  <GlyphCamera :rot-x="35">
    <GlyphScene mode="solid" :cols="120" :rows="36">
      <GlyphMapControls drag wheel />
      <GlyphMesh :polygons="cube" />
    </GlyphScene>
  </GlyphCamera>
</template>

<script setup lang="ts">
import { GlyphCamera, GlyphScene, GlyphMesh, GlyphMapControls } from "@glyphcss/vue";
import { cubePolygons } from "@glyphcss/core";

const cube = cubePolygons({ center: [0, 0, 0], size: 1, color: "#4488ff" });
</script>
```

  

```ts
import { createGlyphCamera, createGlyphScene, createGlyphMapControls } from "glyphcss";
import { cubePolygons } from "@glyphcss/core";

const host = document.querySelector<HTMLElement>("#scene")!;
const camera = createGlyphCamera({ rotX: 35 });
const scene = createGlyphScene(host, { camera, mode: "solid", cols: 120, rows: 36 });
scene.add(cubePolygons({ center: [0, 0, 0], size: 1, color: "#4488ff" }));

const controls = createGlyphMapControls(scene, { drag: true, wheel: true });
```

## First-person controls

Pointer-lock mouselook, WASD / arrow planar move, Space jump, Ctrl crouch — each
axis independently toggleable. Good for walk-through environments and large scenes.
Click the scene to capture the pointer; Esc releases it.

**Requires a perspective camera.** `GlyphFirstPersonControls` throws if attached
to a scene with an orthographic camera. Use `<GlyphPerspectiveCamera>` explicitly
(not `<GlyphCamera>`, which aliases orthographic).

Distances are in world units — size `moveSpeed` / `eyeHeight` to your mesh's scale
(the gallery derives them from the model bbox).

### Props

| Prop | Type | Default | Description |
|---|---|---|---|
| `lookEnabled` | `boolean` | `true` | Pointer-lock mouselook |
| `moveEnabled` | `boolean` | `true` | WASD / arrow planar movement |
| `jumpEnabled` | `boolean` | `true` | Space-bar jump arc |
| `crouchEnabled` | `boolean` | `true` | Ctrl crouch |
| `lookSensitivity` | `number` | `0.15` | Degrees per pixel of mouse |
| `invertY` | `boolean` | `false` | Invert vertical look |
| `moveSpeed` | `number` | `5` | World units per second |
| `jumpVelocity` | `number` | `7` | Initial jump velocity (units/s) |
| `gravity` | `number` | `18` | Gravity (units/s²) |
| `eyeHeight` | `number` | `1.7` | Standing eye height above ground |
| `crouchHeight` | `number` | `1` | Eye height while crouching |
| `groundZ` | `number` | `0` | World Z of the ground plane |
| `minPitch` / `maxPitch` | `number` | `5` / `175` | Pitch clamp (degrees) |

```tsx
import { GlyphPerspectiveCamera, GlyphScene, GlyphMesh, GlyphFirstPersonControls } from "@glyphcss/react";
import { cubePolygons } from "@glyphcss/core";

const floor = cubePolygons({ center: [0, -0.6, 0], size: 4, color: "#334455" });

export function FPVDemo() {
  return (
    <GlyphPerspectiveCamera>
      <GlyphScene mode="solid" cols={140} rows={42}>
        <GlyphFirstPersonControls moveSpeed={3} eyeHeight={1.2} />
        <GlyphMesh polygons={floor} />
      </GlyphScene>
    </GlyphPerspectiveCamera>
  );
}
```

  

```vue
<template>
  <GlyphPerspectiveCamera>
    <GlyphScene mode="solid" :cols="140" :rows="42">
      <GlyphFirstPersonControls :move-speed="3" :eye-height="1.2" />
      <GlyphMesh :polygons="floor" />
    </GlyphScene>
  </GlyphPerspectiveCamera>
</template>

<script setup lang="ts">
import { GlyphPerspectiveCamera, GlyphScene, GlyphMesh, GlyphFirstPersonControls } from "@glyphcss/vue";
import { cubePolygons } from "@glyphcss/core";

const floor = cubePolygons({ center: [0, -0.6, 0], size: 4, color: "#334455" });
</script>
```

  

```ts
import { createGlyphPerspectiveCamera, createGlyphScene, createGlyphFirstPersonControls } from "glyphcss";
import { cubePolygons } from "@glyphcss/core";

const host = document.querySelector<HTMLElement>("#scene")!;
const camera = createGlyphPerspectiveCamera();
const scene = createGlyphScene(host, { camera, mode: "solid", cols: 140, rows: 42 });
scene.add(cubePolygons({ center: [0, -0.6, 0], size: 4, color: "#334455" }));

const controls = createGlyphFirstPersonControls(scene, {
  moveSpeed: 3,
  eyeHeight: 1.2,
});
```
