Cameras and rigs
The 3D camera's pose and lens (perspective or orthographic, infinite far planes, lens shift), picking that matches the GPU exactly, the 2D camera, and ready-made rigs for every kind of game.
scene/Camera.hpp and scene/CameraControllers.hpp, namespace rv::scene. Conventions: right-handed, +Y up, a camera looks down its local −Z; screen pixels count from the top-left of the viewport, x right and y down; angles are radians.
The 3D camera
scene::Camera is a pose and a lens:
| Field | Meaning |
|---|---|
position, orientation |
the pose |
projection |
gfx::Projection::Perspective or Orthographic |
fovY |
perspective field of view |
orthoHeight |
the height an orthographic view shows |
nearZ, farZ |
0 far is infinite for perspective; gfx::kDefaultOrthoFar for orthographic |
lensShift |
an NDC offset of the view’s centre, for a viewport between editor panels |
focusDistance |
where the subject lies; orthographic shadow cascades centre on it |
It gives viewMatrix(), projectionMatrix(), frustum(), screenRay(pixel, viewport) for picking, worldToScreen, pixelSize, framingDistance, and view(), the gfx::CameraView the renderer takes each frame.
Depth is reverse-Z in Vulkan clip space. gfx::projectionMatrix, depthAtDistance, distanceAtDepth and pixelSizeAt in render/View.hpp are the exact functions the GPU uses, so CPU picking matches what is drawn to the pixel. The shaders choose detail by distance; for an orthographic view they use the distance a 70° perspective would show things at the same size, so relief and level of detail look right either way.
The 2D camera
scene::Camera2D: position, rotation, zoom (screen pixels per world unit), viewport, anchor, yDown (world y grows down, like tile editors) and pixelSnap (no shimmer on pixel art). viewProjection() feeds the sprite renderer; it also has worldToScreen, screenToWorld, visibleBounds and zoomAbout(pixel, factor) for wheel zoom about the cursor.
Rigs
Each rig turns a CameraInput (move, look, pan, zoom, boost, snap turns) into a pose with frame-rate-independent smoothing (damp, dampAngle: exponential approach at so many e-foldings a second). cameraInputFromDevices(input, dt) maps the devices conventionally: WASD to move, Q and E down and up, Shift fast, Ctrl slow, mouse look with the right button held (or always when the mouse is captured), middle-button drag to pan, the wheel to zoom, Z and C for snap turns; on a pad, the left stick moves, the right looks, the shoulders go down and up and the triggers zoom.
| Rig | For |
|---|---|
FreeFlyController |
spectator and editor walk-through; the wheel scales speed |
OrbitController |
model viewers, the editor viewport, strategy views: orbit, pan under the cursor, dolly toward a picked point, frame(box) for either projection |
FollowController |
third person over the shoulder (shoulder, swapShoulder), auto-align to the subject’s heading, and a probe that keeps the camera out of walls (snaps in, eases out) |
FirstPersonController |
eye height, a walking bob; the wheel narrows the field of view |
TopDownController |
isometric (a true 35.26° tilt by default) or top-down, orthographic or perspective, snap turns, key, drag and edge pan, follow |
SideScrollController |
2.5D side-on: dead zone, look-ahead, level bounds, orthographic or perspective |
Follow2DController |
the same for Camera2D |
CameraShake |
trauma-based shake for either camera |
rv::scene::Camera camera;
rv::scene::FollowController follow;
follow.shoulder = 0.6f; // metres right of the subject (negative: the left shoulder)
void onUpdate(float dt) override {
auto in = rv::scene::cameraInputFromDevices(input(), dt);
follow.update(camera, heroPosition, in, dt, heroHeading); // the heading is optional
}
The rv_VoxelCameras sample shows one voxel diorama through every rig.
Written from the engine's headers and source as of commit 02e81b2 (2026-11-01).