Results 1 to 3 of 3

Thread: OpenGL Camera Class

  1. #1

    OpenGL Camera Class

    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:
    [pascal]

    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;
    [/pascal]

    But this class is working wrong for me. Could you fix it for me, please? 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

  2. #2

    Re: OpenGL Camera Class

    Quote Originally Posted by Brainer
    I've heard that a quaternion-based camera system is good and the most efficient. Is that true?
    Yes.

  3. #3

    OpenGL Camera Class

    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.

    Code:
    // 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;
    This is my game project - Top Down City:
    http://www.pascalgamedevelopment.com...y-Topic-Reboot

    My OpenAL audio wrapper with Intelligent Source Manager to use unlimited:
    http://www.pascalgamedevelopment.com...source+manager

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •