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.