Camera Unit

The camera unit of program TesterGameEngine

```unit uCamera;
{

use this file except in compliance with the License, as described at
}
interface

uses
Classes, SysUtils, SDL, GL, GLU, uGlobalVariables;

type
Vector = class(TObject)
public
x, y, z: real;
constructor Create(newx, newy, newz: real);
constructor normalise();
end;

type
Camera = class(TObject)
public
xvector, yvector, zvector, posn: Vector;
constructor Create(newx, newy, newz: integer);
procedure MoveForward(speed: real);
//moves the camera forwards by amount relative to camera angle
procedure MoveRight(speed: real);
//moves the camera right by amount relative to camera angle
procedure MoveUp(speed: real);
//moves the camera up by amount relative to camera angle
procedure SetPosn(xposn, yposn, zposn: real);
procedure Rotate(speed: real);
procedure SetUp(Up: Vector);
procedure Activate(Width, Height: integer; mouserotate: boolean);
//Sets the OpenGL camera to this camera's position and orientation
end;

function cross(a, b: Vector): Vector;
function Testcollision(speed: real; direction: integer): boolean;

var
MainCamera, ActiveCamera: Camera;

implementation

constructor Vector.Create(newx, newy, newz: real);
begin //constructs a vector object
x := newx;
y := newy;
z := newz;
end;

constructor Vector.normalise(); //returns the normal of a given vector
var
len: real;
begin
len := sqrt((x * x) + (y * y) + (z * z));
x /= len;
y /= len;
z /= len;
end;

function cross(a, b: Vector): Vector; //get the cross product of two vectors
begin
Result := Vector.Create(0.0, 0.0, 0.0);
Result.x := (a.y * b.z) - (a.z * b.y);
Result.y := (a.x * b.z) - (a.z * b.x);
Result.z := (a.x * b.y) - (a.y * b.x);
end;

constructor Camera.Create(newx, newy, newz: integer);
begin  //Construct a Camera object.
posn := Vector.Create(newx, newy, newz);
xvector := Vector.Create(1.0, 0, 0);
yvector := Vector.Create(0, 1.0, 0);
zvector := Vector.Create(0, 0, 1.0);
end;

procedure Camera.Rotate(speed: real); //rotates the perspective of the camera
var
vVector: Vector;
begin
vVector := Vector.Create(zvector.x - posn.x, zvector.y - posn.y, zvector.z - posn.z);
//Find the vector between the point the camera is looking at and the point the camera itself is at.
zvector.z := (posn.z + sin(speed) * vVector.x + cos(speed) * vVector.z);
//These two lines move the point the camera is looking at around the circumference
zvector.x := (posn.x + cos(speed) * vVector.x - sin(speed) * vVector.z);
//of a circle with a radius of the length of vVector and centre at the camera position.
mousex := 0;
mousey := 0;
end;

procedure Camera.MoveForward(speed: real);
//Moves the camera towards or away from the point the camera is looking at
var
vVector: Vector;
begin
if not Testcollision(speed, 0) then
begin
// Get the view vector.
vVector := Vector.Create(zvector.x - posn.x, zvector.y - posn.y, zvector.z - posn.z);

// forward positive speed and backward negative speed
posn.x := posn.x + vVector.x * speed;
//moves the camera's x and z positions along vVector with given speed
posn.z := posn.z + vVector.z * speed;
zvector.x := zvector.x + vVector.x * speed;
zvector.z := zvector.z + vVector.z * speed;
end;
end;

procedure Camera.MoveRight(speed: real);
//Moves the camera left or right relative to the perspective
var
vVector, vOrthoVector: Vector;
begin
if not TestCollision(speed, 1) then
begin
vVector := Vector.Create(zvector.x - posn.x, zvector.y - posn.y, zvector.z - posn.z);
vOrthoVector := Vector.Create(0.0, 0.0, 0.0);
//Sets up a new vector to be orthogonal (at right angles) to the view vector
vOrthoVector.x := -vVector.z; //makes vOrthoVector orthogonal to vVector
vOrthoVector.z := vVector.x;

// left positive cameraspeed and right negative cameraspeed.
posn.x := posn.x + vOrthoVector.x * speed;
posn.z := posn.z + vOrthoVector.z * speed;
zvector.x := zvector.x + vOrthoVector.x * speed;
zvector.z := zvector.z + vOrthoVector.z * speed;
end;
end;

procedure Camera.MoveUp(speed: real);
//Moves the camera up or down relative to the camera's 'up' vector
begin
posn.x := posn.x + yvector.x * speed;
posn.y := posn.y + yvector.y * speed;
posn.z := posn.z + yvector.z * speed;
zvector.y += yvector.y * speed;
end;

procedure Camera.SetPosn(xposn, yposn, zposn: real);
//Instantly jumps the camera to the specified location
begin
//make sure the direction you are looking remains constant
zvector.x := xposn + (zvector.x - posn.x);
zvector.y := yposn + (zvector.y - posn.y);
zvector.z := zposn + (zvector.z - posn.z);
//move the camera to the specified location
posn.x := xposn;
posn.y := yposn;
posn.z := zposn;
end;

procedure Camera.Activate(Width, Height: integer; mouserotate: boolean);
//Updates the perspective and position of the camera within the scene (updates the view ready for it to be rerendered)
var
mid_x: integer;
mid_y: integer;
angle_y: real = 0.0;
angle_z: real = 0.0;
begin
mid_x := Width div 2; //gets the x and y position of the centre of the scene
mid_y := Height div 2;
SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
SDL_WarpMouse(mid_x, mid_y); //centre the mouse
SDL_EventState(SDL_MOUSEMOTION, SDL_ENABLE);
// Get the direction of rotation by reading the mouse position relative to the centre of the scene.
if mouserotate = True then
begin
angle_y := mousex / 400;
angle_z := mousey / 500;
// The higher the constant, the faster the camera looks around.
zvector.y += angle_z * 4;
// limit the rotation around the x-axis (up/down)
if ((zvector.y - posn.y) > 8) then
zvector.y := posn.y + 8;
if ((zvector.y - posn.y) < -8) then
zvector.y := posn.y - 8;
Rotate(angle_y); // Rotate
end;
gluLookAt(posn.x, posn.y, posn.z, zvector.x, zvector.y, zvector.z, yvector.x,
yvector.y, yvector.z); //makes the OpenGL perspective look at a certain point
end;

procedure Camera.SetUp(Up: Vector);
//Sets the 'up' vector of the camera
begin
yvector.x := Up.x;
yvector.y := Up.y;
yvector.z := Up.z;
end;

function Testcollision(speed: real; direction: integer): boolean;
//Checks if the camera will collide with anything if it were to move
var
vVector: Vector;
i: integer;
begin
Testcollision := False;
if direction = 0 then
vVector := Vector.Create(ActiveCamera.zvector.x - ActiveCamera.posn.x,
ActiveCamera.zvector.y - ActiveCamera.posn.y,
ActiveCamera.zvector.z - ActiveCamera.posn.z);
//if the camera is moving forwards/back set the velocity Vector accordingly
if direction = 1 then
vVector := Vector.Create(-(ActiveCamera.zvector.z - ActiveCamera.posn.z),
ActiveCamera.zvector.y - ActiveCamera.posn.y,
ActiveCamera.zvector.x - ActiveCamera.posn.x);
//same for if the camera is moving left/right
speed *= 25;
for i := 0 to 99 do
begin //for loop to read through the array of walls
if walls[i].orientation = 0 then
begin //if the wall varies in the x-direction
if (walls[i].x1 <= ActiveCamera.posn.x + (vVector.x * speed)) and
(walls[i].x2 >= ActiveCamera.posn.x + (vVector.x * speed)) then
begin //check whether the camera will be within the wall
if (walls[i].y1 <= ActiveCamera.posn.y) and
(walls[i].y2 >= ActiveCamera.posn.y) then
begin
if (activecamera.posn.z > walls[i].z1) and
(ActiveCamera.posn.z + (vVector.z * speed) < walls[i].z1) then
begin
Testcollision := True;
break;
end; //if so then return true
if (activecamera.posn.z < walls[i].z1) and
(ActiveCamera.posn.z + (vVector.z * speed) > walls[i].z1) then
begin
Testcollision := True;
break;
end;
end;
end;
end;

if walls[i].orientation = 1 then
begin //if the wall varies in the z-direction
if (walls[i].z1 <= ActiveCamera.posn.z + (vVector.z * speed)) and
(walls[i].z2 >= ActiveCamera.posn.z + (vVector.z * speed)) then
begin //again, check whether we will be in the wall
if (walls[i].y1 <= ActiveCamera.posn.y) and
(walls[i].y2 >= ActiveCamera.posn.y) then
begin
if (activecamera.posn.x > walls[i].x1) and
(ActiveCamera.posn.x + (vVector.x * speed) < walls[i].x1) then
begin
Testcollision := True;
break;
end;
if (activecamera.posn.x < walls[i].x1) and
(ActiveCamera.posn.x + (vVector.x * speed) > walls[i].x1) then
begin
Testcollision := True;
break;
end;
end;
end;
end;
end;
end;

end.
```
Programming - a skill for life!

by Steven Binns: Y12 Age ~17