# Positions, directions and 3D
Everything in audio-code lives in one 3D world: physics bodies, positioned sounds and the
listener. This page explains how positions are written (`vec3`), what the numbers mean, and
how to make a game that uses all three dimensions: flying, swimming, climbing, or sounds
above and below the player.
## `vec3(x, y, z)`: what the three numbers mean
A position is a `Vec3`: three numbers, written `vec3(x, y, z)`. Each number is a distance
in **meters** from the origin, the point `vec3(0.0, 0.0, 0.0)` (also written `Vec3::ZERO`).
| `x` (first) | east | west | right / left |
| `y` (second) | north | south | up / down on the page (forward / back) |
| `z` (third) | up | down | height above the ground |
Examples:
```rust
vec3(0.0, 0.0, 0.0) // the origin
vec3(5.0, 0.0, 0.0) // 5 m east of the origin
vec3(-5.0, 0.0, 0.0) // 5 m west
vec3(0.0, 10.0, 0.0) // 10 m north
vec3(3.0, -2.0, 0.0) // 3 m east and 2 m south
vec3(0.0, 0.0, 4.0) // 4 m straight above the origin
vec3(10.0, 15.0, 8.0) // 10 m east, 15 m north, 8 m up: a bird in a tree
```
Write the numbers with a decimal point (`5.0`, not `5`), because they are `f32` values.
You can read the parts back with `.x`, `.y` and `.z`:
```rust
let pos = ctx.physics.position(player);
let height = pos.z;
ctx.speak(format!("{:.0} meters east, {:.0} meters north", pos.x, pos.y));
```
### The same type for directions and speeds
A `Vec3` can also describe a **direction** or a **velocity**. Then it does not say where
something is, but which way and how fast it goes:
```rust
ctx.physics.set_velocity(car, vec3(0.0, 14.0, 0.0)); // 14 m/s towards north
ctx.physics.apply_impulse(ball, vec3(0.0, 0.0, 6.0)); // a kick straight up
ctx.physics.move_by(player, vec3(1.0, 0.0, 0.0)); // 1 m east, right now
```
### Calculating with positions
```rust
let a = vec3(1.0, 2.0, 0.0);
let b = vec3(4.0, 6.0, 0.0);
a + b // vec3(5.0, 8.0, 0.0): move a by b
b - a // vec3(3.0, 4.0, 0.0): the way from a to b
(b - a).length() // 5.0: how far that is, in meters
a.distance(b) // 5.0: the same in one call
a * 2.0 // vec3(2.0, 4.0, 0.0): twice as far from the origin
(b - a).normalize() // vec3(0.6, 0.8, 0.0): only the direction, 1 m long
a + vec3(0.0, 0.0, 2.0) // 2 m above a
```
`length` and `distance` count all three axes, so a sound 3 m east and 4 m up is 5 m away.
### Directions from angles
Instead of writing directions by hand, you usually get them from an angle:
```rust
math::forward(90.0) // vec3(1.0, 0.0, 0.0): east, 1 m long
math::right(0.0) // facing north, right is east
math::forward_3d(0.0, 45.0) // north and 45° up
pos + math::forward(yaw) * 3.0 // 3 m ahead of someone facing `yaw`
```
## Angles: facing (yaw) and pitch
Two angles describe where someone looks. Both are in degrees.
**Facing** (also called yaw) is the compass direction, measured clockwise from north:
```text
0 north
|
270 west ----+---- 90 east
|
180 south
```
**Pitch** is how far someone looks up or down: 0 is level with the horizon, 90 is straight
up, -90 is straight down, 30 is a little up.
```rust
ctx.physics.turn(player, 30.0); // facing 30° further right (negative = left)
ctx.physics.tilt(player, 30.0); // look 30° further up (negative = down), stops at ±90
ctx.physics.set_facing(player, 180.0);
ctx.physics.set_pitch(player, 0.0);
ctx.physics.look_at(player, target_pos); // face a point: sets facing and pitch
let (yaw, pitch) = math::look_at(from, to);
math::elevation(from, to); // how many degrees `to` is above `from`
```
Shapes never rotate. Facing and pitch only change where the body moves with
`move_forward`, where the ears point when the body is followed, and how `describe`
describes things.
## Three kinds of games
| Top down (maze, map) | x and y, z stays 0 | off | `turn`, `move_forward`, `strafe` |
| Side scroller | x (left/right) and z (up/down), y stays 0, facing stays 0 | on | `set_velocity`, `jump` |
| Full 3D (flying, swimming, space, a building with floors) | all three | off for flying, on for walking | `turn`, `tilt`, `move_forward`, `move_up` |
In a side scroller the player always faces north, so east (`+x`) is on the right ear and
`z` is up.
## Moving in 3D
```rust
ctx.physics.move_forward(drone, 1.0); // 1 m where it looks, including up or down
ctx.physics.move_up(drone, 1.0); // 1 m straight up, -1.0 straight down
ctx.physics.strafe(drone, 1.0); // 1 m to the right, always level
ctx.physics.move_by(drone, vec3(0.0, 0.0, -0.5)); // any direction
```
All of these stop at walls, floors and ceilings and return a `MoveResult`. With a pitch of
0 (the default), `move_forward` stays level, so walking games do not change. If a walking
player may look up and down but should still walk level, move it with
`move_by(id, math::forward(facing) * distance)`.
`ctx.input.movement_3d()` reads the keys for 3D movement: arrows or WASD for `x` (right)
and `y` (forward), Page Up or E for up and Page Down or Q for down (`z`).
```rust
let dir = ctx.input.movement_3d();
let facing = ctx.physics.facing(drone);
let delta = math::right(facing) * dir.x + math::forward(facing) * dir.y + vec3(0.0, 0.0, dir.z);
ctx.physics.move_by(drone, delta * 4.0 * ctx.time.delta()); // 4 m/s
```
## Sound in 3D
Positioned sounds always use the full 3D distance. The listener can also look up and down:
```rust
ctx.audio.set_listener(pos, yaw); // looking level
ctx.audio.set_listener_3d(pos, yaw, pitch); // looking up or down
ctx.follow(drone); // uses the body's facing and pitch
```
How a sound's direction is heard on headphones:
| right / left | louder in that ear |
| ahead | centered, clear |
| behind | centered, muffled and up to 30 % quieter |
| above | centered, clear |
| below | centered, a little dull and up to 15 % quieter |
Above, below, ahead and behind are measured from the head: when the player looks straight
up, a sound above is "ahead", and a sound at the horizon in front is "below". Stereo
headphones cannot really place a sound above or below, so tell the player the height as
well, for example with `describe`.
## Describing positions
`describe` speaks the height when it differs by a meter or more:
```rust
ctx.physics.describe(player, bird);
// "2 o'clock, 9 meters" same height
// "12 o'clock, 11 meters, 4 meters above" higher
// "directly below, 5 meters" nothing to the side
```
The clock direction uses the facing (12 o'clock is straight ahead), the distance is the
straight line in 3D.
## A complete example
`examples/flight.rs` is a small 3D game: you fly a drone through a hall and look for birds
that sit at different heights. It uses `tilt`, `move_forward` with pitch, `move_up`,
`describe` with heights and the listener's pitch. Run it with
`cargo run --example flight` and use headphones.