# 3D scenes

> Write 3D in TSX with Bevy's names: a camera, lights, meshes and materials, moved by the frame, with 2D titles over them in the same composition.

You write 3D the way you write the rest of a composition: components whose props are computed from the frame. raea
turns them into Bevy entities (meshes, lights, cameras, glTF models, post-processing) and draws them under the page,
so titles, captions and a score sit over the 3D in the same file. You never write Rust or a render loop. A render and
a live play of the same file draw the same thing, so what you check in a render is what plays.

## A turntable

```tsx file=turntable.tsx
// check: frames 0,45,89
import { AbsoluteFill, Composition, Easing, interpolate, registerRoot, useCurrentFrame, useVideoConfig } from "raea";
import { Cylinder, Entity } from "raea/3d";
import { StandardMaterial } from "raea/pbr";
import { Cuboid, Mesh3d } from "raea/mesh";
import { AmbientLight, DirectionalLight } from "raea/light";
import { Quat, Transform } from "raea/transform";
import { Camera3d } from "raea/camera";

function Turntable() {
  const f = useCurrentFrame();
  const { fps, durationInFrames } = useVideoConfig();
  const turn = (f / fps) * (Math.PI / 3); // a sixth of a turn a second
  const z = interpolate(f, [0, durationInFrames - 1], [7, 5], { easing: Easing.out(Easing.cubic), extrapolateLeft: "clamp", extrapolateRight: "clamp" });
  return (
    <AbsoluteFill style={{ background: "#14161c" }}>
      <Camera3d transform={Transform.fromXyz(0, 1.6, z).lookingAt([0, 0.5, 0])} projection={{ Perspective: { fov: (40 * Math.PI) / 180 } }} />
      <AmbientLight brightness={160} />
      <DirectionalLight transform={Transform.fromXyz(3, 5, 4).lookingAt([0, 0, 0])} illuminance={11200} shadow_maps_enabled />
      <Entity transform={Transform.IDENTITY.withRotation(Quat.fromEuler("XYZ", 0, turn, 0))}>
        <Mesh3d transform={Transform.fromXyz(0, -0.1, 0)}>
          <Cylinder radius={1.4} half_height={0.1} />
          <StandardMaterial base_color="#2b2f3a" perceptual_roughness={0.6} />
        </Mesh3d>
        <Mesh3d transform={Transform.fromXyz(0, 0.6, 0)}>
          <Cuboid half_size={[0.45, 0.6, 0.45]} />
          <StandardMaterial base_color="#e8b04a" perceptual_roughness={0.35} metallic={0.2} />
        </Mesh3d>
      </Entity>
      <div style={{ position: "absolute", top: 80, width: "100%", textAlign: "center", fontFamily: "Inter", fontWeight: 800, fontSize: 72, color: "#ffffff" }}>
        Made to be seen
      </div>
    </AbsoluteFill>
  );
}

registerRoot(() => <Composition id="turntable" component={Turntable} width={1080} height={1080} fps={30} durationInFrames={90} />);
```

```sh
raea render turntable.tsx --at 0,45,89 -o frames/
raea render turntable.tsx -o turntable.mp4
```

What each part does:

- **`Camera3d`** is the camera: where it is (`transform`) and its lens (`projection`, with the field of view in
  radians). The first camera in the tree is the one that renders.
- **Lights**: `AmbientLight` fills the shadows, `DirectionalLight` is a sun (its brightness in lux, here 11,200),
  `shadow_maps_enabled` turns its shadows on.
- **`Entity`** groups parts so they move together. Turning the group turns the plinth and the box on it.
- **`Mesh3d`** is a visible thing: a shape (`Cylinder`, `Cuboid`, `Sphere`, `Torus` and others from `raea/mesh`) and
  a material (`StandardMaterial` from `raea/pbr`, with `base_color`, `perceptual_roughness` and `metallic`).
- **`Transform`** places things: `Transform.fromXyz(x, y, z)`, `.lookingAt([x, y, z])`, and rotations from
  `Quat.fromEuler`. Y is up and units are metres, as in Bevy.
- **The `div` after the 3D** is ordinary 2D layout, drawn over the scene.

## The rules for 3D

1. **Every value comes from the frame.** A spin is `(frame / fps) * speed`, not a value kept between frames. That is
   what keeps a render and a live play the same.
2. **Rotations are in radians.** `Quat.fromEuler("XYZ", 0, Math.PI / 2, 0)` is a quarter turn about Y.
3. **Colours are hex strings.** CSS variables don't reach the 3D.
4. **Name parts that come and go** (`<Mesh3d name="lid">`), so raea matches them across frames.
5. **A part's shape is fixed for its life.** Animate its transform, colour, opacity, emission, roughness or metalness;
   to change its shape, swap in another named part.

## Checking a scene

```sh
raea scene lint turntable.tsx          # parts floating, sunk into the floor, or clipping each other
raea scene light turntable.tsx         # how the scene is lit, and whether the subject is exposed well
raea render turntable.tsx --review review/   # fixed cameras (hero, front, right, top, iso, low) in colour and clay
```

`--review` writes the same scene from six fixed cameras, in colour and as untextured clay, so you can see the shapes
and the light without the materials getting in the way.

## The 3D guides

The 3D guides go topic by topic, each with a complete composition to start from. Read one or two at a time:

```sh
raea guide bevy               # the map of 3D topics
raea guide bevy/lights        # one topic
```

| Topic | Covers |
|---|---|
| `bevy/scene` | The camera, camera moves, groups, 2D under and over 3D |
| `bevy/lights` | Sun, bulbs, spots, ambient light, shadows, brightness |
| `bevy/materials` | Colour, metal, glass, glow, clearcoat, textures, toon shading and outlines, decals |
| `bevy/meshes` | Primitives, modelled solids, blended organic shapes, trees, terrain, rocks, 3D text |
| `bevy/animation` | Spins, springs, reveals, model animation clips |
| `bevy/characters` | Bodies posed by goals, motion capture, walking to a spot |
| `bevy/rooms` | Interiors, furniture along walls, windows, daylight, sound placed in the scene |
| `bevy/games` | 3D games: state, input, many entities, collisions, state machines |
| `bevy/assets` | glTF models, pictures in space, where files go |
| `bevy/blender` | Fixing an asset in Blender first: scale, pivot, heavy meshes, FBX to glTF |
| `bevy/splats` | Gaussian splats trained from footage, and animating them |
| `bevy/liquids` | Liquids and goo, simulated and baked |
| `bevy/post` | Bloom, fog, tone mapping, exposure, lens effects, motion blur |
| `bevy/limits` | How much a scene can hold, and what isn't there yet |

The same topics ship in the raea skill as `3d/*.md`.
