Camera Unit

The camera unit of program TesterGameEngine

unit uCamera;
{
    Copyright (c) 2013 Steven Binns

    Licensed under the Apache License, Version 2.0 (the "License"); you may not
    use this file except in compliance with the License, as described at
    http://www.apache.org/licenses/ and http://www.pp4s.co.uk/licenses/
}
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