Ragnavoxel docs

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).