Camera
A first-person camera: a position plus a yaw/pitch heading, producing the view matrix each frame. Yaw and pitch are held instead of a look-at target because that is what input drives -- turning is a change in yaw, not a new target point. Pitch is clamped just short of straight up/down, since at exactly vertical the view direction becomes parallel to 'up' and no camera basis exists. Movement is horizontal: MoveForward walks along the heading with the pitch ignored, so looking down does not sink you into the floor. That is what a walking character wants; a flying one would use ForwardVector directly.
Example
camera := Camera->New(Vector3->New(0.0, 1.7, 5.0)); # eye height
camera->Turn(0.03);
camera->MoveForward(0.1);
shader->SetMatrix4("view", camera->GetViewMatrix());Operations
- New
- ForwardVector
- GetProjection
- GetViewMatrix
- GetViewMatrixInto
- GetViewProjection
- GetViewProjectionInto
- GroundForward
- GroundRight
- HasProjection
- Look
- LookAtPoint
- MoveForward
- MoveRight
- PlaceLookingAt
- ScreenRay
- SetAspect
- SetPerspective
- SetPerspectiveDegrees
- SetPosition
- Turn
- Unproject
ForwardVector #
The direction the camera is looking, including pitch.
method : public : ForwardVector() ~ Vector3Return
| Type | Description |
|---|---|
| Vector3 | a unit vector |
GetProjection #
method : public : GetProjection() ~ Float[]Return
| Type | Description |
|---|---|
| Float | the projection matrix. BORROWED -- do not write to it or keep it across a SetAspect |
GetViewMatrix #
The view matrix for this frame.
method : public : GetViewMatrix() ~ Float[]Return
| Type | Description |
|---|---|
| Float | a 4x4 view matrix, column-major |
GetViewMatrixInto #
The view matrix, into a buffer the caller owns. The allocating form makes four objects every call -- three Vector3 and the Float[16] -- and a draw loop calls it every frame. This makes none: the basis is computed straight into the destination.
method : public : GetViewMatrixInto(out:Float[]) ~ NilParameters
| Name | Type | Description |
|---|---|---|
| out | Float | destination, at least 16 elements |
GetViewProjection #
Projection times view, rebuilt from the current eye. Returns the camera's own buffer rather than a fresh array, so a draw loop calling this every frame allocates nothing. Do not write to it, and do not hold it across another call.
method : public : GetViewProjection() ~ Float[]Return
| Type | Description |
|---|---|
| Float | a 16-element column-major matrix, or Nil if SetPerspective has not been called |
GetViewProjectionInto #
Projection times view, ready to hand to Scene->Draw. The two-step version -- build a projection, then multiply it by the view -- is what both 3D examples did, and it is the last per-frame allocation in a draw loop that is otherwise clean.
method : public : GetViewProjectionInto(out:Float[], projection:Float[], scratch:Float[]) ~ NilParameters
| Name | Type | Description |
|---|---|---|
| out | Float | destination, at least 16 elements |
| projection | Float | a projection matrix, built once and kept |
| scratch | Float | a second 16-element buffer this may use; must not be out |
GroundForward #
The heading on the ground plane, pitch ignored.
method : public : GroundForward() ~ Vector3Return
| Type | Description |
|---|---|
| Vector3 | a unit vector with no vertical component |
GroundRight #
The camera's right-hand direction on the ground plane.
method : public : GroundRight() ~ Vector3Return
| Type | Description |
|---|---|
| Vector3 | a unit vector with no vertical component |
HasProjection #
method : public : HasProjection() ~ BoolReturn
| Type | Description |
|---|---|
| Bool | true once SetPerspective has been called |
Look #
Look up or down. Clamped to just under +/- 90 degrees, because at exactly vertical the view direction is parallel to 'up' and the camera basis collapses.
method : public : Look(radians:Float) ~ NilParameters
| Name | Type | Description |
|---|---|---|
| radians | Float | positive looks up |
LookAtPoint #
Turn to face a point, without moving. Camera is a yaw-and-pitch camera, which is right for a first-person view and awkward for anything that watches something: an orbiting or tracking shot knows where it wants to look, not what angles that is. This converts. Leaves roll alone, because there isn't any -- the up vector stays world-up, which is what keeps a scene from feeling tilted.
method : public : LookAtPoint(target:Vector3) ~ NilParameters
| Name | Type | Description |
|---|---|---|
| target | Vector3 | the point to face |
MoveForward #
Walk along the heading. Vertical component ignored, so looking down does not push the eye through the floor.
method : public : MoveForward(distance:Float) ~ NilParameters
| Name | Type | Description |
|---|---|---|
| distance | Float | how far; negative walks backwards |
MoveRight #
Strafe sideways.
method : public : MoveRight(distance:Float) ~ NilParameters
| Name | Type | Description |
|---|---|---|
| distance | Float | how far; negative goes left |
New # constructor
New(position:Vector3)Parameters
| Name | Type | Description |
|---|---|---|
| position | Vector3 | where the eye starts |
PlaceLookingAt #
Place the eye and face a point in one call -- an orbiting or tracking shot.
method : public : PlaceLookingAt(position:Vector3, target:Vector3) ~ NilParameters
| Name | Type | Description |
|---|---|---|
| position | Vector3 | where the eye goes |
| target | Vector3 | what it looks at |
ScreenRay #
The ray running through a point on the screen, in world space. Scene->Raycast could already answer "what is in front of the camera", but only along the camera's own forward vector -- the middle of the screen. A mouse pick, a click-to-select, a drag onto a tile: all of them start from a PIXEL, and there was no way to turn one into a direction. Pass mouse coordinates and the WINDOW size, not the drawable size. Both come from the same space -- GetMouseX against GetWidth -- and mixing points with high-DPI pixels doubles the offset on a Retina display in a way that looks like a calibration error rather than a unit mismatch.
method : public : ScreenRay(x:Int, y:Int, width:Int, height:Int) ~ Vector3Parameters
| Name | Type | Description |
|---|---|---|
| x | Int | pixel across, from the left |
| y | Int | pixel down, from the TOP |
| width | Int | the window's width |
| height | Int | the window's height |
Return
| Type | Description |
|---|---|
| Vector3 | a unit direction; the camera's forward vector if the view cannot be inverted, which keeps callers off a Nil check |
SetAspect #
Update the aspect ratio, rebuilding the projection only if it changed. Cheap enough to call unconditionally every frame, which is the point: a window can be resized at any moment and the alternative is tracking a generation counter in every program.
method : public : SetAspect(aspect:Float) ~ NilParameters
| Name | Type | Description |
|---|---|---|
| aspect | Float | width divided by height |
SetPerspective #
Give the camera its own projection, so GetViewProjection can build the whole matrix without the caller keeping buffers.
method : public : SetPerspective(fov:Float, aspect:Float, near:Float, far:Float) ~ NilParameters
| Name | Type | Description |
|---|---|---|
| fov | Float | vertical field of view, in RADIANS |
| aspect | Float | width divided by height -- window->GetAspect() |
| near | Float | near plane; too small a value wastes depth precision |
| far | Float | far plane |
SetPerspectiveDegrees #
Same, in degrees, with the near and far planes most programs want.
method : public : SetPerspectiveDegrees(degrees:Float, aspect:Float) ~ NilParameters
| Name | Type | Description |
|---|---|---|
| degrees | Float | vertical field of view in degrees |
| aspect | Float | width divided by height |
SetPosition #
Place the eye directly.
method : public : SetPosition(position:Vector3) ~ NilParameters
| Name | Type | Description |
|---|---|---|
| position | Vector3 | the new position |
Turn #
Turn left or right.
method : public : Turn(radians:Float) ~ NilParameters
| Name | Type | Description |
|---|---|---|
| radians | Float | positive turns one way, negative the other |
Unproject #
Where a point on the near or far plane lands in world space. The perspective divide is the part that matters: the inverse of a projection is not an affine transform, so w comes back as something other than 1 and dividing by it is what turns clip space into a world position.
method : private : Unproject() ~ Vector3Parameters
| Name | Type | Description |
|---|---|---|