PDA

View Full Version : OpenGL Camera Class



Brainer
02-12-2007, 09:11 AM
System: Windows XP SP1, Pentium D 805 2,66 GHz 2 GB RAM, GeForce 7600 GS
Compiler/IDE: Delphi 2007
Libraries/API: JEDI-SDL
---
Hello again. :)

This time I've got a different problem. I want to make a camera class for my engine. So far, I've got this:


type
{ .: TCoordinates :. }
TCoordinates = class(TPersistent)
private
{ Private declarations }
FVec: TAffineVector;
function GetVecAsString(): String;
public
{ Public declarations }
constructor Create();
procedure Assign(Source: TPersistent); override;

property X: Single read FVec[0] write FVec[0];
property Y: Single read FVec[1] write FVec[1];
property Z: Single read FVec[2] write FVec[2];

property AsVector: TAffineVector read FVec;
property AsString: String read GetVecAsString;
end;

{ .: TCamera :. }
TCamera = class(TPersistent)
private
{ Private declarations }
FRight: TCoordinates;
FViewDir: TCoordinates;
FUp: TCoordinates;
FPos: TCoordinates;
procedure SetPos(const Value: TCoordinates);
procedure SetRight(const Value: TCoordinates);
procedure SetUp(const Value: TCoordinates);
procedure SetViewDir(const Value: TCoordinates);
protected
{ Protected declarations }
RotatedX, RotatedY, RotatedZ: Single;
public
{ Public declarations }
constructor Create();
destructor Destroy(); override;

procedure Assign(Source: TPersistent); override;

procedure Move(const ADirection: TAffineVector);
procedure RotateX(const ARotAngle: Single);
procedure RotateY(const ARotAngle: Single);
procedure RotateZ(const ARotAngle: Single);
procedure MoveForward(const ADistance: Single);
procedure MoveUpward(const ADistance: Single);
procedure StrafeRight(const ADistance: Single);

procedure Render();

property Position: TCoordinates read FPos write SetPos;
property ViewDirection: TCoordinates read FViewDir write SetViewDir;
property Right: TCoordinates read FRight write SetRight;
property Up: TCoordinates read FUp write SetUp;
end;

{ TCamera }

procedure TCamera.Assign(Source: TPersistent);
begin
if (Source is TCamera) then
begin
FPos.Assign(TCamera(Source).Position);
FViewDir.Assign(TCamera(Source).ViewDirection);
FRight.Assign(TCamera(Source).Right);
FUp.Assign(TCamera(Source).Up);
end else
inherited;
end;

constructor TCamera.Create;
begin
inherited Create();

FPos := TCoordinates.Create();
FViewDir := TCoordinates.Create();
FViewDir.Z := -1.0;
FRight := TCoordinates.Create();
FRight.X := 1.0;
FUp := TCoordinates.Create();
FUp.Y := 1.0;

RotatedX := 0.0;
RotatedY := 0.0;
RotatedZ := 0.0;
end;

destructor TCamera.Destroy;
begin
FPos.Free();
FViewDir.Free();
FRight.Free();
FUp.Free();

inherited Destroy();
end;

procedure TCamera.Move(const ADirection: TAffineVector);
begin
with FPos do
begin
X := X + ADirection[0];
Y := Y + ADirection[1];
Z := Z + ADirection[2];
end;
end;

procedure TCamera.MoveForward(const ADistance: Single);
var
V: TAffineVector;
begin
V := FPos.AsVector;
V := VectorAdd(V, VectorScale(FViewDir.AsVector, -ADistance));
FPos.X := V[0];
FPos.Y := V[1];
FPos.Z := V[2];
end;

procedure TCamera.MoveUpward(const ADistance: Single);
var
V: TAffineVector;
begin
V := FPos.AsVector;
V := VectorAdd(V, VectorScale(FUp.AsVector, ADistance));
FPos.X := V[0];
FPos.Y := V[1];
FPos.Z := V[2];
end;

procedure TCamera.Render;
var
V: TAffineVector;
begin
V := VectorAdd(FPos.AsVector, FViewDir.AsVector);

gluLookAt(FPos.X, FPos.Y, FPos.Z, V[0], V[1], V[2], FUp.X, FUp.Y, FUp.Z);
end;

procedure TCamera.RotateX(const ARotAngle: Single);
var
V, V1, V2: TAffineVector;
begin
RotatedX := RotatedX + ARotAngle;

V := FViewDir.AsVector;
V1 := FUp.AsVector;
V := VectorScale(V, Cos(ARotAngle * PIDIV180));
V1 := VectorScale(V1, Sin(ARotAngle * PIDIV180));
V := VectorNormalize(VectorAdd(V, V1));

V2 := VectorCrossProduct(V, FRight.AsVector);
V2 := VectorScale(V2, -1.0);

FViewDir.X := V[0];
FViewDir.Y := V[1];
FViewDir.Z := V[2];

FUp.X := V2[0];
FUp.Y := V2[1];
FUp.Z := V2[2];
end;

procedure TCamera.RotateY(const ARotAngle: Single);
var
V, V1, V2: TAffineVector;
begin
RotatedY := RotatedY + ARotAngle;

V := FViewDir.AsVector;
V1 := FRight.AsVector;
V := VectorScale(V, Cos(ARotAngle * PIDIV180));
V1 := VectorScale(V1, Sin(ARotAngle * PIDIV180));
V := VectorNormalize(VectorSubtract(V, V1));

V2 := VectorCrossProduct(V, FUp.AsVector);

FViewDir.X := V[0];
FViewDir.Y := V[1];
FViewDir.Z := V[2];

FRight.X := V2[0];
FRight.Y := V2[1];
FRight.Z := V2[2];
end;

procedure TCamera.RotateZ(const ARotAngle: Single);
var
V, V1, V2: TAffineVector;
begin
RotatedZ := RotatedZ + ARotAngle;

V := FRight.AsVector;
V1 := FUp.AsVector;
V := VectorScale(V, Cos(ARotAngle * PIDIV180));
V1 := VectorScale(V1, Sin(ARotAngle * PIDIV180));
V := VectorNormalize(VectorAdd(V, V1));

V2 := VectorCrossProduct(FViewDir.AsVector, V);
V2 := VectorScale(V2, -1.0);

FRight.X := V[0];
FRight.Y := V[1];
FRight.Z := V[2];

FUp.X := V2[0];
FUp.Y := V2[1];
FUp.Z := V2[2];
end;

procedure TCamera.SetPos(const Value: TCoordinates);
begin
FPos := Value;
end;

procedure TCamera.SetRight(const Value: TCoordinates);
begin
FRight := Value;
end;

procedure TCamera.SetUp(const Value: TCoordinates);
begin
FUp := Value;
end;

procedure TCamera.SetViewDir(const Value: TCoordinates);
begin
FViewDir := Value;
end;

procedure TCamera.StrafeRight(const ADistance: Single);
var
V: TAffineVector;
begin
V := FPos.AsVector;
V := VectorAdd(V, VectorScale(FRight.AsVector, ADistance));
FPos.X := V[0];
FPos.Y := V[1];
FPos.Z := V[2];
end;


But this class is working wrong for me. Could you fix it for me, please? :wink: Or maybe you know other solutions of the problem? :? I've heard that a quaternion-based camera system is good and the most efficient. Is that true?

Thank you in advance

Mirage
02-12-2007, 11:12 AM
I've heard that a quaternion-based camera system is good and the most efficient. Is that true?

Yes. ;)

JernejL
02-12-2007, 12:28 PM
First of all: very helpful topic title indeed.

Secondly, if you don't want your program to drag all that GLU junk with it just for glulookat, etc.. you may want to consider this pascal code as alternative, translated from SGI'S opengl implementation sample.


// this is gluPickMatrix equivalent translated from ogl-sample.

procedure PickMatrix(x, y, deltax, deltay: GLfloat; viewport: array of integer);
begin
if &#40;deltax <= 0&#41; and &#40;deltay <= 0&#41; then
exit;

&#40;* Translate and scale the picked region to the entire window *&#41;
glTranslatef&#40;
&#40;viewport&#91;2&#93; - 2 * &#40;x - viewport&#91;0&#93;&#41;&#41; / deltax,
&#40;viewport&#91;3&#93; - 2 * &#40;y - viewport&#91;1&#93;&#41;&#41; / deltay,
0&#41;;

glScalef&#40;viewport&#91;2&#93; / deltax, viewport&#91;3&#93; / deltay, 1.0&#41;;
end;

// this is gluPerspective equivalent translated from ogl-sample.
// matrix should be double but i use single precision

procedure Perspective&#40;fovy, aspect, zNear, zFar&#58; single&#41;;
var
m&#58; TMatrix4f;
sine, cotangent, deltaZ, radians&#58; single;

begin
radians &#58;= fovy / 2 * Pi / 180;

deltaZ &#58;= zFar - zNear;
sine &#58;= sin&#40;radians&#41;;
if &#40;&#40;deltaZ = 0&#41; and &#40;sine = 0&#41; and &#40;aspect = 0&#41;&#41; then
exit;

cotangent &#58;= COS&#40;radians&#41; / sine;

m &#58;= Identity;
m&#91;0&#93;&#91;0&#93; &#58;= cotangent / aspect;
m&#91;1&#93;&#91;1&#93; &#58;= cotangent;
m&#91;2&#93;&#91;2&#93; &#58;= -&#40;zFar + zNear&#41; / deltaZ;
m&#91;2&#93;&#91;3&#93; &#58;= -1;
m&#91;3&#93;&#91;2&#93; &#58;= -2 * zNear * zFar / deltaZ;
m&#91;3&#93;&#91;3&#93; &#58;= 0;
glMultMatrixf&#40;@m&#91;0&#93;&#91;0&#93;&#41;;
end;

// this is glulookat equivalent translated from ogl-sample.
// matrix should be single but i use single precision

procedure normalize&#40;var v&#58; array of single&#41;;
var
r&#58; single;
begin
r &#58;= sqrt&#40;v&#91;0&#93; * v&#91;0&#93; + v&#91;1&#93; * v&#91;1&#93; + v&#91;2&#93; * v&#91;2&#93;&#41;;

if &#40;r = 0&#41; then
exit;

v&#91;0&#93; &#58;= v&#91;0&#93; / r;
v&#91;1&#93; &#58;= v&#91;1&#93; / r;
v&#91;2&#93; &#58;= v&#91;2&#93; / r;
end;

procedure crossp&#40;var v1, v2, Result&#58; array of single&#41;;
begin
Result&#91;0&#93; &#58;= v1&#91;1&#93; * v2&#91;2&#93; - v1&#91;2&#93; * v2&#91;1&#93;;
Result&#91;1&#93; &#58;= v1&#91;2&#93; * v2&#91;0&#93; - v1&#91;0&#93; * v2&#91;2&#93;;
Result&#91;2&#93; &#58;= v1&#91;0&#93; * v2&#91;1&#93; - v1&#91;1&#93; * v2&#91;0&#93;;
end;

// translated from ogl-sample
procedure LookAt&#40;eyex, eyey, eyez, centerx, centery, centerz, upx, upy, upz&#58; single&#41;;
var
forw, side, up&#58; array&#91;0..2&#93; of single;
m&#58; TMatrix4f;
begin
forw&#91;0&#93; &#58;= centerx - eyex;
forw&#91;1&#93; &#58;= centery - eyey;
forw&#91;2&#93; &#58;= centerz - eyez;

up&#91;0&#93; &#58;= upx;
up&#91;1&#93; &#58;= upy;
up&#91;2&#93; &#58;= upz;

normalize&#40;forw&#41;;

&#40;* Side&#58;= forw x up *&#41;
crossp&#40;forw, up, side&#41;;
normalize&#40;side&#41;;

&#40;* Recompute up as&#58; up&#58;= side x forw *&#41;
crossp&#40;side, forw, up&#41;;

m &#58;= Identity;
m&#91;0&#93;&#91;0&#93; &#58;= side&#91;0&#93;;
m&#91;0&#93;&#91;1&#93; &#58;= side&#91;1&#93;;
m&#91;0&#93;&#91;2&#93; &#58;= side&#91;2&#93;;

m&#91;1&#93;&#91;0&#93; &#58;= up&#91;0&#93;;
m&#91;1&#93;&#91;1&#93; &#58;= up&#91;1&#93;;
m&#91;1&#93;&#91;2&#93; &#58;= up&#91;2&#93;;

m&#91;2&#93;&#91;0&#93; &#58;= -forw&#91;0&#93;;
m&#91;2&#93;&#91;1&#93; &#58;= -forw&#91;1&#93;;
m&#91;2&#93;&#91;2&#93; &#58;= -forw&#91;2&#93;;

glMultMatrixf&#40;@m&#41;;
glTranslated&#40;-eyex, -eyey, -eyez&#41;;
end;